diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,12 @@
 # Revision history for waargonaut
 
+## 0.5.0.0  -- 2018-12-18
+
+* Changed internal builder from `ByteString` to `Text` builder.
+* Fixed bug for going from `JString` <-> `Text`, was breaking round-trip.
+* Removed instances of `AsJString` for `Text` and `ByteString`, replaced with more correct `Prism` and some better functions.
+* Added regression tests for round tripping text and bytestring (char8).
+
 ## 0.4.2.0  -- 2018-11-29
 
 * Improved pretty printing of CursorHistory by condensing multiple numeric movements and removing the single movements following searching for keys.
diff --git a/src/Waargonaut/Decode.hs b/src/Waargonaut/Decode.hs
--- a/src/Waargonaut/Decode.hs
+++ b/src/Waargonaut/Decode.hs
@@ -31,6 +31,7 @@
 
     -- * Helpers
   , DI.ppCursorHistory
+  , parseWith
 
     -- * Cursors
   , withCursor
@@ -69,6 +70,8 @@
   , scientific
   , integral
   , string
+  , strictByteString
+  , lazyByteString
   , unboundedChar
   , boundedChar
   , text
@@ -90,16 +93,16 @@
 import           Control.Lens                              (Cons, Lens', Prism',
                                                             Snoc, cons, lens,
                                                             matching, modifying,
-                                                            preview, snoc,
+                                                            over, preview, snoc,
                                                             traverseOf, view,
                                                             ( # ), (.~), (^.),
-                                                            _Wrapped)
+                                                            _Left, _Wrapped)
 
 import           Prelude                                   (Bool, Bounded, Char,
                                                             Eq, Int, Integral,
-                                                            String,
-                                                            fromIntegral, (-),
-                                                            (==))
+                                                            Show, String,
+                                                            fromIntegral, show,
+                                                            (-), (==))
 
 import           Control.Applicative                       (Applicative (..))
 import           Control.Category                          ((.))
@@ -137,11 +140,13 @@
                                                             successor', zero')
 
 import           Data.Text                                 (Text)
+import qualified Data.Text                                 as Text
 
-import           Data.ByteString.Char8                     (ByteString)
-import qualified Data.ByteString.Char8                     as BS8
+import           Text.Parser.Char                          (CharParsing)
 
-import           Waargonaut.Types
+import           Data.ByteString                           (ByteString)
+import qualified Data.ByteString.Char8                     as BS8
+import qualified Data.ByteString.Lazy                      as BL
 
 import           HaskellWorks.Data.Positioning             (Count)
 import qualified HaskellWorks.Data.Positioning             as Pos
@@ -155,11 +160,11 @@
 import           HaskellWorks.Data.Json.Cursor             (JsonCursor (..))
 import qualified HaskellWorks.Data.Json.Cursor             as JC
 
-
 import           Waargonaut.Decode.Error                   (AsDecodeError (..),
                                                             DecodeError (..),
                                                             Err (..))
 import           Waargonaut.Decode.ZipperMove              (ZipperMove (..))
+import           Waargonaut.Types
 
 import qualified Waargonaut.Decode.Internal                as DI
 
@@ -492,13 +497,6 @@
 -- | A version of 'fromKey' that returns its result in 'Maybe'. If the key is
 -- not present in the object, 'Nothing' is returned. If the key is present,
 -- decoding will be performed as with 'fromKey'.
---
--- For example, if a key could be absent and could be null if present,
--- it could be decoded as follows:
---
--- @
--- join <$> fromKeyOptional "key" (maybeOrNull text)
--- @
 fromKeyOptional
   :: Monad f
   => Text
@@ -515,6 +513,13 @@
 -- | A version of 'atKey' that returns its result in 'Maybe'. If the key is
 -- not present in the object, 'Nothing' is returned. If the key is present,
 -- decoding will be performed as with 'atKey'.
+--
+-- For example, if a key could be absent and could be null if present,
+-- it could be decoded as follows:
+--
+-- @
+-- join \<$> atKeyOptional "key" (maybeOrNull text)
+-- @
 atKeyOptional
   :: Monad f
   => Text
@@ -681,6 +686,27 @@
   runPureDecode d parseFn
   . mkCursor
 
+-- | Helper function to handle wrapping up a parse failure using the given
+-- parsing function. Intended to be used with the 'runDecode' or 'simpleDecode'
+-- functions.
+--
+-- @
+-- import Data.Attoparsec.ByteString (parseOnly)
+--
+-- simpleDecode (list int) (parseWith (parseOnly parseWaargonaut)) "[1,1,2]"
+-- @
+--
+parseWith
+  :: ( CharParsing f
+     , Show e
+     )
+  => (f a -> i -> Either e a)
+  -> f a
+  -> i
+  -> Either DecodeError a
+parseWith f p =
+  over _Left (ParseFailed . Text.pack . show) . f p
+
 -- | Similar to the 'simpleDecode' function, however this function expects
 -- you've already converted your input to a 'JCurs'.
 runPureDecode
@@ -766,6 +792,14 @@
 -- | Decode a 'String' value.
 string :: Monad f => Decoder f String
 string = atCursor "string" DI.string'
+
+-- | Decode a strict 'ByteString' value.
+strictByteString :: Monad f => Decoder f ByteString
+strictByteString = atCursor "strict bytestring" DI.strictByteString'
+
+-- | Decode a lazy 'ByteString' value.
+lazyByteString :: Monad f => Decoder f BL.ByteString
+lazyByteString = atCursor "lazy bytestring" DI.lazyByteString'
 
 -- | Decode a 'Char' value that is equivalent to a Haskell 'Char' value, as Haskell 'Char' supports a wider range than JSON.
 unboundedChar :: Monad f => Decoder f Char
diff --git a/src/Waargonaut/Decode/Internal.hs b/src/Waargonaut/Decode/Internal.hs
--- a/src/Waargonaut/Decode/Internal.hs
+++ b/src/Waargonaut/Decode/Internal.hs
@@ -25,6 +25,8 @@
   , int'
   , text'
   , string'
+  , lazyByteString'
+  , strictByteString'
   , unboundedChar'
   , boundedChar'
   , bool'
@@ -64,6 +66,9 @@
 import           Data.Semigroup                ((<>))
 import           Data.Sequence                 (Seq, fromList)
 
+import           Data.ByteString               (ByteString)
+import qualified Data.ByteString.Lazy          as BL
+import qualified Data.ByteString.Lazy.Builder  as BB
 import           Data.Text                     (Text)
 
 import           Data.Map                      (Map)
@@ -79,9 +84,10 @@
 import           Natural                       (Natural, _Natural)
 
 import           Waargonaut.Types              (AsJType (..), JString,
+                                                jCharBuilderByteStringL,
                                                 jNumberToScientific,
                                                 jsonAssocKey, jsonAssocVal,
-                                                _JChar, _JString)
+                                                _JChar, _JStringText)
 import           Waargonaut.Types.CommaSep     (toList)
 import           Waargonaut.Types.JChar        (jCharToUtf8Char)
 
@@ -288,13 +294,22 @@
 prismDOrFail' e p d c =
   runDecoder' (L.preview p <$> d) c <!?> e
 
--- | Try to decode a 'Text' value from some 'Json' or value.
+-- | Try to decode a 'Text' value from some 'Json' or value. This will fail if
+-- the input value is not a valid UTF-8 'Text' value, as checked by the
+-- 'Data.Text.Encoding.decodeUtf8'' function.
 text' :: AsJType a ws a => a -> Maybe Text
-text' = L.preview (_JStr . _1 . L.re _JString)
+text' = L.preview (_JStr . _1 . _JStringText)
 
 -- | Try to decode a 'String' value from some 'Json' or value.
 string' :: AsJType a ws a => a -> Maybe String
 string' = L.preview (_JStr . _1 . _Wrapped . L.to (V.toList . V.map (_JChar L.#)))
+
+strictByteString' :: AsJType a ws a => a -> Maybe ByteString
+strictByteString' = fmap BL.toStrict . lazyByteString'
+
+lazyByteString' :: AsJType a ws a => a -> Maybe BL.ByteString
+lazyByteString' = L.preview (_JStr . _1 . _Wrapped . L.to mkBS)
+  where mkBS = BB.toLazyByteString . foldMap jCharBuilderByteStringL
 
 -- | Decoder for a 'Char' value that cannot contain values in the range U+D800
 -- to U+DFFF. This decoder will fail if the 'Char' is outside of this range.
diff --git a/src/Waargonaut/Decode/Traversal.hs b/src/Waargonaut/Decode/Traversal.hs
--- a/src/Waargonaut/Decode/Traversal.hs
+++ b/src/Waargonaut/Decode/Traversal.hs
@@ -389,7 +389,7 @@
       >=> Z.within WT.jsonAssocVal
     )
   where
-    shuffleToKey cu = Z.within WT.jsonAssocKey cu ^? L._Just . Z.focus . L.re WT._JString
+    shuffleToKey cu = Z.within WT.jsonAssocKey cu ^? L._Just . Z.focus . WT._JStringText
       >>= Bool.bool (Just cu) (Z.rightward cu >>= shuffleToKey) . (/=k)
 
     intoElems = WT._JObj . L._1 . L._Wrapped . WT._CommaSeparated . L._2 . L._Just
diff --git a/src/Waargonaut/Decode/ZipperMove.hs b/src/Waargonaut/Decode/ZipperMove.hs
--- a/src/Waargonaut/Decode/ZipperMove.hs
+++ b/src/Waargonaut/Decode/ZipperMove.hs
@@ -12,6 +12,8 @@
 import           Data.Text                     (Text)
 import qualified Data.Text                     as Text
 
+import           Data.Semigroup                ((<>))
+
 import           Natural                       (Natural)
 
 import           Text.PrettyPrint.Annotated.WL (Doc, (<+>))
@@ -34,15 +36,15 @@
 -- 'Waargonaut.Decode.Internal.CursorHistory'' to improve the readability of the errors.
 ppZipperMove :: ZipperMove -> Doc a
 ppZipperMove m = case m of
-  U              -> WL.text "up/"
-  D              -> WL.text "down\\"
+  U            -> WL.text "up/" <> WL.linebreak
+  D            -> WL.text "down\\" <> WL.linebreak
 
-  (L n)          -> WL.text "-<-" <+> ntxt n
-  (R n)          -> WL.text "->-" <+> ntxt n
+  L n          -> WL.text "-<-" <+> ntxt n
+  R n          -> WL.text " ->-" <+> ntxt n
 
-  (DAt k)        -> WL.text "into\\" <+> itxt "key" k
-  (Item t)       -> WL.text "-::" <+> itxt "item" t
-  (BranchFail t) -> WL.text "(attempted: " <+> ntxt t <+> WL.text ")"
+  DAt k        -> WL.text "into\\" <+> itxt "key" k <> WL.linebreak
+  Item t       -> WL.text "-::" <+> itxt "item" t <> WL.linebreak
+  BranchFail t -> WL.text "(attempted: " <+> ntxt t <+> WL.text ")" <> WL.linebreak
   where
     itxt t k' = WL.parens (WL.text t <+> WL.colon <+> WL.text (Text.unpack k'))
     ntxt n'   = WL.parens (WL.char 'i' <+> WL.char '+' <+> WL.text (show n'))
diff --git a/src/Waargonaut/Encode.hs b/src/Waargonaut/Encode.hs
--- a/src/Waargonaut/Encode.hs
+++ b/src/Waargonaut/Encode.hs
@@ -32,6 +32,7 @@
   , integral
   , scientific
   , bool
+  , string
   , text
   , null
   , either
@@ -66,6 +67,7 @@
   , integral'
   , scientific'
   , bool'
+  , string'
   , text'
   , null'
   , either'
@@ -92,7 +94,8 @@
 import qualified Control.Lens                         as L
 
 import           Prelude                              (Bool, Int, Integral,
-                                                       Monad, fromIntegral, fst)
+                                                       Monad, String,
+                                                       fromIntegral, fst)
 
 import           Data.Foldable                        (Foldable, foldr, foldrM)
 import           Data.Function                        (const, flip, ($), (&))
@@ -112,13 +115,12 @@
 import           Data.Monoid                          (Monoid, mempty)
 import           Data.Semigroup                       (Semigroup)
 
-import qualified Data.ByteString.Builder              as BB
-import           Data.ByteString.Lazy                 (ByteString)
-
 import           Data.Map                             (Map)
 import qualified Data.Map                             as Map
 
 import           Data.Text                            (Text)
+import qualified Data.Text.Lazy                       as LT
+import qualified Data.Text.Lazy.Builder               as TB
 
 import           Waargonaut.Encode.Types              (Encoder, Encoder',
                                                        ObjEncoder, ObjEncoder',
@@ -132,10 +134,12 @@
 import           Waargonaut.Types                     (AsJType (..),
                                                        JAssoc (..), JObject,
                                                        Json, MapLikeObj (..),
-                                                       WS, textToJString,
+                                                       WS, stringToJString,
                                                        toMapLikeObj, wsRemover,
                                                        _JNumberInt,
-                                                       _JNumberScientific)
+                                                       _JNumberScientific,
+                                                       _JStringText)
+
 import           Waargonaut.Types.Json                (waargonautBuilder)
 
 
@@ -153,15 +157,15 @@
   :: Applicative f
   => Encoder f a
   -> a
-  -> f ByteString
+  -> f LT.Text
 simpleEncodeNoSpaces enc =
-  fmap (BB.toLazyByteString . waargonautBuilder wsRemover) . runEncoder enc
+  fmap (TB.toLazyText . waargonautBuilder wsRemover) . runEncoder enc
 
 -- | As per 'simpleEncodeNoSpaces' but specialised the 'f' to 'Data.Functor.Identity' and remove it.
 simplePureEncodeNoSpaces
   :: Encoder Identity a
   -> a
-  -> ByteString
+  -> LT.Text
 simplePureEncodeNoSpaces enc =
   runIdentity . simpleEncodeNoSpaces enc
 
@@ -204,9 +208,13 @@
 bool :: Applicative f => Encoder f Bool
 bool = encToJsonNoSpaces _JBool id
 
+-- | Encode a 'String'
+string :: Applicative f => Encoder f String
+string = encToJsonNoSpaces _JStr stringToJString
+
 -- | Encode a 'Text'
 text :: Applicative f => Encoder f Text
-text = encToJsonNoSpaces _JStr textToJString
+text = encToJsonNoSpaces _JStr (_JStringText #)
 
 -- | Encode an explicit 'null'.
 null :: Applicative f => Encoder f ()
@@ -299,6 +307,10 @@
 bool' :: Encoder' Bool
 bool' = bool
 
+-- | As per 'string' but with the 'f' specialised to 'Data.Functor.Identity'.
+string' :: Encoder' String
+string' = string
+
 -- | As per 'text' but with the 'f' specialised to 'Data.Functor.Identity'.
 text' :: Encoder' Text
 text' = text
@@ -586,7 +598,7 @@
   -> JObject WS Json
   -> f (JObject WS Json)
 onObj k b encB o = (\j -> o & _Wrapped L.%~ L.cons j)
-  . JAssoc (textToJString k) mempty mempty <$> runEncoder encB b
+  . JAssoc (_JStringText # k) mempty mempty <$> runEncoder encB b
 
 -- | Encode key value pairs as a JSON object, allowing duplicate keys.
 keyValuesAsObj
diff --git a/src/Waargonaut/Test.hs b/src/Waargonaut/Test.hs
new file mode 100644
--- /dev/null
+++ b/src/Waargonaut/Test.hs
@@ -0,0 +1,37 @@
+module Waargonaut.Test
+  ( roundTripSimple
+  ) where
+
+import qualified Data.Text.Encoding      as Text
+import qualified Data.Text.Lazy          as TextL
+
+import           Data.ByteString         (ByteString)
+
+import           Text.Parser.Char        (CharParsing)
+
+import           Waargonaut.Types        (Json, parseWaargonaut)
+
+import           Waargonaut.Encode       (Encoder)
+import qualified Waargonaut.Encode       as E
+
+import           Waargonaut.Decode       (CursorHistory, Decoder)
+import qualified Waargonaut.Decode       as D
+import           Waargonaut.Decode.Error (DecodeError)
+
+roundTripSimple
+  :: ( Eq b
+     , Monad f
+     , CharParsing f
+     , Monad g
+     , Show e
+     )
+  => (f Json -> ByteString -> Either e Json)
+  -> Encoder g b
+  -> Decoder g b
+  -> b
+  -> g (Either (DecodeError, CursorHistory) Bool)
+roundTripSimple f e d a = do
+  encodedA <- E.simpleEncodeNoSpaces e a
+  (fmap . fmap) (== a) $ D.runDecode d
+    (D.parseWith f parseWaargonaut)
+    (D.mkCursor . Text.encodeUtf8 . TextL.toStrict $ encodedA)
diff --git a/src/Waargonaut/Types/CommaSep.hs b/src/Waargonaut/Types/CommaSep.hs
--- a/src/Waargonaut/Types/CommaSep.hs
+++ b/src/Waargonaut/Types/CommaSep.hs
@@ -78,8 +78,8 @@
 
 import           Data.Functor.Identity   (Identity (..))
 
-import           Data.ByteString.Builder (Builder)
-import qualified Data.ByteString.Builder as BB
+import           Data.Text.Lazy.Builder (Builder)
+import qualified Data.Text.Lazy.Builder as TB
 
 import           Text.Parser.Char        (CharParsing, char)
 import qualified Text.Parser.Combinators as C
@@ -108,7 +108,7 @@
 
 -- | Builder for UTF8 Comma
 commaBuilder :: Builder
-commaBuilder = BB.charUtf8 ','
+commaBuilder = TB.singleton ','
 {-# INLINE commaBuilder #-}
 
 -- | Parse a single comma (,)
@@ -370,7 +370,7 @@
   -> CommaSeparated ws a
   -> Builder
 commaSeparatedBuilder op fin wsB aB (CommaSeparated lws sepElems) =
-  BB.charUtf8 op <> wsB lws <> maybe mempty buildElems sepElems <> BB.charUtf8 fin
+  TB.singleton op <> wsB lws <> maybe mempty buildElems sepElems <> TB.singleton fin
   where
     elemBuilder
       :: Foldable f
diff --git a/src/Waargonaut/Types/JArray.hs b/src/Waargonaut/Types/JArray.hs
--- a/src/Waargonaut/Types/JArray.hs
+++ b/src/Waargonaut/Types/JArray.hs
@@ -37,7 +37,7 @@
 import           Data.Semigroup            (Semigroup (..))
 import           Data.Traversable          (Traversable)
 
-import           Data.ByteString.Builder   (Builder)
+import           Data.Text.Lazy.Builder   (Builder)
 
 import           Text.Parser.Char          (CharParsing, char)
 
diff --git a/src/Waargonaut/Types/JChar.hs b/src/Waargonaut/Types/JChar.hs
--- a/src/Waargonaut/Types/JChar.hs
+++ b/src/Waargonaut/Types/JChar.hs
@@ -27,7 +27,10 @@
   , parseJChar
   , parseJCharEscaped
   , parseJCharUnescaped
-  , jCharBuilder
+  , jCharBuilderWith
+  , jCharBuilderTextL
+  , jCharBuilderByteStringL
+
   , jCharToChar
 
     -- * Conversion
@@ -35,49 +38,51 @@
   , jCharToUtf8Char
   ) where
 
-import           Prelude                     (Char, Eq, Int, Ord, Show,
-                                              otherwise, quotRem, (&&),
-                                              (*), (+), (-), (/=), (<=), (==),
-                                              (>=))
+import           Prelude                      (Char, Eq, Int, Ord, Show,
+                                               otherwise, quotRem, (&&), (*),
+                                               (+), (-), (/=), (<=), (==), (>=))
 
-import           Control.Category            (id, (.))
-import           Control.Lens                (Lens', Prism', Rewrapped,
-                                              Wrapped (..), failing, has, iso,
-                                              prism, prism', to, ( # ), (^?),
-                                              _Just)
+import           Control.Category             (id, (.))
+import           Control.Lens                 (Lens', Prism', Rewrapped,
+                                               Wrapped (..), failing, has, iso,
+                                               prism, prism', to, ( # ), (^?),
+                                               _Just)
 
-import           Control.Applicative         (pure, (*>), (<$>), (<*>), (<|>))
-import           Control.Monad               ((=<<))
+import           Control.Applicative          (pure, (*>), (<$>), (<*>), (<|>))
+import           Control.Monad                ((=<<))
 
-import           Control.Error.Util          (note)
+import           Control.Error.Util           (note)
 
-import           Data.Bits                   ((.&.))
+import           Data.Bits                    ((.&.))
 
-import           Data.Char                   (chr, ord)
-import           Data.Either                 (Either (..))
-import           Data.Foldable               (Foldable, any, asum, foldMap,
-                                              foldl)
-import           Data.Function               (const, ($))
-import           Data.Functor                (Functor)
-import           Data.Maybe                  (Maybe (..), fromMaybe)
-import           Data.Semigroup              ((<>))
-import           Data.Traversable            (Traversable, traverse)
+import           Data.Char                    (chr, ord)
+import           Data.Either                  (Either (..))
+import           Data.Foldable                (Foldable, any, asum, foldMap,
+                                               foldl)
+import           Data.Function                (const, ($))
+import           Data.Functor                 (Functor)
+import           Data.Maybe                   (Maybe (..), fromMaybe)
+import           Data.Monoid                  (Monoid)
+import           Data.Semigroup               (Semigroup, (<>))
+import           Data.Traversable             (Traversable, traverse)
 
-import qualified Data.Text.Internal          as Text
+import qualified Data.Text.Internal           as Text
 
-import           Data.Digit                  (HeXDigit, HeXaDeCiMaL)
-import qualified Data.Digit                  as D
+import           Data.Digit                   (HeXDigit, HeXaDeCiMaL)
+import qualified Data.Digit                   as D
 
-import           Data.ByteString.Builder     (Builder)
-import qualified Data.ByteString.Builder     as BB
+import           Data.Text.Lazy.Builder       (Builder)
+import qualified Data.Text.Lazy.Builder       as TB
 
-import           Waargonaut.Types.Whitespace (Whitespace (..),
-                                              escapedWhitespaceChar,
-                                              unescapedWhitespaceChar,
-                                              _WhitespaceChar)
+import qualified Data.ByteString.Lazy.Builder as BB
 
-import           Text.Parser.Char            (CharParsing, char, satisfy)
+import           Waargonaut.Types.Whitespace  (Whitespace (..),
+                                               escapedWhitespaceChar,
+                                               unescapedWhitespaceChar,
+                                               _WhitespaceChar)
 
+import           Text.Parser.Char             (CharParsing, char, satisfy)
+
 -- $setup
 -- >>> :set -XOverloadedStrings
 -- >>> import Control.Monad (return)
@@ -445,19 +450,30 @@
     (WhiteSpace ws) -> _WhiteSpace # ws
     Hex hexDig4     -> hexDigit4ToChar hexDig4
 
--- | Create a 'Builder' for the given 'JChar'.
-jCharBuilder
-  :: HeXaDeCiMaL digit
-  => JChar digit
-  -> Builder
-jCharBuilder (UnescapedJChar (JCharUnescaped c)) = BB.charUtf8 c
-jCharBuilder (EscapedJChar jca) = BB.charUtf8 '\\' <> case jca of
-    QuotationMark           -> BB.charUtf8 '"'
-    ReverseSolidus          -> BB.charUtf8 '\\'
-    Solidus                 -> BB.charUtf8 '/'
-    Backspace               -> BB.charUtf8 'b'
-    (WhiteSpace ws)         -> BB.charUtf8 (unescapedWhitespaceChar ws)
-    Hex (HexDigit4 a b c d) -> BB.charUtf8 'u' <> foldMap hexChar [a,b,c,d]
+jCharBuilderWith
+  :: ( Monoid builder
+     , Semigroup builder
+     , HeXaDeCiMaL digit
+     )
+  => (Char -> builder)
+  -> JChar digit
+  -> builder
+jCharBuilderWith f (UnescapedJChar (JCharUnescaped c)) = f c
+jCharBuilderWith f (EscapedJChar jca) = f '\\' <> case jca of
+    QuotationMark           -> f '"'
+    ReverseSolidus          -> f '\\'
+    Solidus                 -> f '/'
+    Backspace               -> f 'b'
+    (WhiteSpace ws)         -> f (unescapedWhitespaceChar ws)
+    Hex (HexDigit4 a b c d) -> f 'u' <> foldMap hexChar [a,b,c,d]
   where
     hexChar =
-      BB.charUtf8 . (D.charHeXaDeCiMaL #)
+      f . (D.charHeXaDeCiMaL #)
+
+-- | Create a Lazy 'Text' 'Data.Text.Lazy.Builder' for the given 'JChar'.
+jCharBuilderTextL :: HeXaDeCiMaL digit => JChar digit -> Builder
+jCharBuilderTextL = jCharBuilderWith TB.singleton
+
+-- | Create a Lazy 'ByteString' 'Data.ByteString.Lazy.Builder' for a given 'JChar'
+jCharBuilderByteStringL :: HeXaDeCiMaL digit => JChar digit -> BB.Builder
+jCharBuilderByteStringL = jCharBuilderWith BB.charUtf8
diff --git a/src/Waargonaut/Types/JNumber.hs b/src/Waargonaut/Types/JNumber.hs
--- a/src/Waargonaut/Types/JNumber.hs
+++ b/src/Waargonaut/Types/JNumber.hs
@@ -31,45 +31,48 @@
   , jNumberToScientific
   ) where
 
-import           Prelude                 (Bool (..), Eq, Int, Ord, Show, Integral, abs,
-                                          fromIntegral, maxBound, minBound,
-                                          negate, (-), (<), (>), (||))
+import           Prelude                    (Bool (..), Eq, Int, Integral, Ord,
+                                             Show, abs, fromIntegral, maxBound,
+                                             minBound, negate, (-), (<), (>),
+                                             (||))
 
-import           Data.Scientific         (Scientific)
-import qualified Data.Scientific         as Sci
+import           Data.Scientific            (Scientific)
+import qualified Data.Scientific            as Sci
 
-import           Control.Category        (id, (.))
-import           Control.Lens            (Lens', Prism', Rewrapped,
-                                          Wrapped (..), iso, prism, ( # ), (^?),
-                                          _Just, _Wrapped)
+import           Control.Category           (id, (.))
+import           Control.Lens               (Lens', Prism', Rewrapped,
+                                             Wrapped (..), iso, prism, ( # ),
+                                             (^?), _Just, _Wrapped)
 
-import           Control.Applicative     (pure, (*>), (<$), (<$>), (<*>))
-import           Control.Monad           (Monad, (=<<))
+import           Control.Applicative        (pure, (*>), (<$), (<$>), (<*>))
+import           Control.Monad              (Monad, (=<<))
 
-import           Control.Error.Util      (note)
+import           Control.Error.Util         (note)
 
-import           Data.Either             (Either (..))
-import           Data.Function           (const, ($))
-import           Data.Functor            (fmap)
-import           Data.Maybe              (Maybe (..), fromMaybe, isJust, maybe)
-import           Data.Monoid             (mappend, mempty)
-import           Data.Semigroup          ((<>))
-import           Data.Traversable        (traverse)
-import           Data.Tuple              (uncurry)
+import           Data.Either                (Either (..))
+import           Data.Function              (const, ($))
+import           Data.Functor               (fmap)
+import           Data.Maybe                 (Maybe (..), fromMaybe, isJust,
+                                             maybe)
+import           Data.Monoid                (mappend, mempty)
+import           Data.Semigroup             ((<>))
+import           Data.Traversable           (traverse)
+import           Data.Tuple                 (uncurry)
 
-import           Data.List.NonEmpty      (NonEmpty ((:|)), some1)
-import qualified Data.List.NonEmpty      as NE
+import           Data.List.NonEmpty         (NonEmpty ((:|)), some1)
+import qualified Data.List.NonEmpty         as NE
 
-import           Data.Foldable           (asum, foldMap, length)
+import           Data.Foldable              (asum, foldMap, length)
 
-import           Data.Digit              (DecDigit)
-import qualified Data.Digit              as D
+import           Data.Digit                 (DecDigit)
+import qualified Data.Digit                 as D
 
-import           Text.Parser.Char        (CharParsing, char)
-import           Text.Parser.Combinators (many, optional)
+import           Text.Parser.Char           (CharParsing, char)
+import           Text.Parser.Combinators    (many, optional)
 
-import           Data.ByteString.Builder (Builder)
-import qualified Data.ByteString.Builder as BB
+import           Data.Text.Lazy.Builder     (Builder)
+import qualified Data.Text.Lazy.Builder     as TB
+import qualified Data.Text.Lazy.Builder.Int as TB
 
 -- $setup
 -- >>> :set -XOverloadedStrings
@@ -81,7 +84,7 @@
 -- >>> import qualified Data.Digit as D
 -- >>> import Waargonaut.Decode.Error (DecodeError)
 -- >>> import Data.ByteString.Lazy (toStrict)
--- >>> import Data.ByteString.Builder (toLazyByteString)
+-- >>> import Data.Text.Lazy.Builder (toLazyText)
 -- >>> import Utils
 
 -- | Represent a JSON "int"
@@ -340,8 +343,8 @@
 eBuilder
   :: E
   -> Builder
-eBuilder Ee = BB.charUtf8 'e'
-eBuilder EE = BB.charUtf8 'E'
+eBuilder Ee = TB.singleton 'e'
+eBuilder EE = TB.singleton 'E'
 
 -- | Parse the fractional component of a JSON number.
 --
@@ -409,8 +412,8 @@
 getExpSymbol
   :: Maybe Bool
   -> Builder
-getExpSymbol (Just True)  = BB.charUtf8 '-'
-getExpSymbol (Just False) = BB.charUtf8 '+'
+getExpSymbol (Just True)  = TB.singleton '-'
+getExpSymbol (Just False) = TB.singleton '+'
 getExpSymbol _            = mempty
 
 -- | Builder for a list of digits.
@@ -418,7 +421,10 @@
   :: NonEmpty DecDigit
   -> Builder
 digitsBuilder =
-  foldMap (BB.int8Dec . (D.integralDecimal #))
+  foldMap (int8 . (D.integralDecimal #))
+  where
+    int8 :: Int -> Builder
+    int8 = TB.decimal
 
 -- | Builder for the exponent portion.
 expBuilder
@@ -481,13 +487,13 @@
 
 -- | Printing of JNumbers
 --
--- >>> toLazyByteString $ jNumberBuilder (JNumber {_minus = False, _numberint = JIntInt D.DecDigit3 [], _frac = Just (Frac (D.DecDigit4 :| [D.DecDigit5])), _expn = Just (Exp {_ex = Ee, _minusplus = Just False, _expdigits = D.DecDigit1 :| [D.DecDigit0]})})
+-- >>> toLazyText $ jNumberBuilder (JNumber {_minus = False, _numberint = JIntInt D.DecDigit3 [], _frac = Just (Frac (D.DecDigit4 :| [D.DecDigit5])), _expn = Just (Exp {_ex = Ee, _minusplus = Just False, _expdigits = D.DecDigit1 :| [D.DecDigit0]})})
 -- "3.45e+10"
 --
--- >>> toLazyByteString $ jNumberBuilder (JNumber {_minus = True, _numberint = JIntInt D.DecDigit3 [], _frac = Just (Frac (D.DecDigit4 :| [D.DecDigit5])), _expn = Just (Exp {_ex = Ee, _minusplus = Just True, _expdigits = D.DecDigit0 :| [D.x2]})})
+-- >>> toLazyText $ jNumberBuilder (JNumber {_minus = True, _numberint = JIntInt D.DecDigit3 [], _frac = Just (Frac (D.DecDigit4 :| [D.DecDigit5])), _expn = Just (Exp {_ex = Ee, _minusplus = Just True, _expdigits = D.DecDigit0 :| [D.x2]})})
 -- "-3.45e-02"
 --
--- >>> toLazyByteString $ jNumberBuilder (JNumber {_minus = False, _numberint = JIntInt D.DecDigit0 [D.DecDigit0], _frac = Nothing, _expn = Nothing})
+-- >>> toLazyText $ jNumberBuilder (JNumber {_minus = False, _numberint = JIntInt D.DecDigit0 [D.DecDigit0], _frac = Nothing, _expn = Nothing})
 -- "00"
 --
 jNumberBuilder
@@ -496,9 +502,9 @@
 jNumberBuilder (JNumber sign digs mfrac mexp) =
   s <> digits <> frac' <> expo
   where
-    s      = if sign then BB.charUtf8 '-' else mempty
+    s      = if sign then TB.singleton '-' else mempty
     digits = digitsBuilder . jIntToDigits $ digs
-    frac'  = foldMap (mappend (BB.charUtf8 '.') . fracBuilder) mfrac
+    frac'  = foldMap (mappend (TB.singleton '.') . fracBuilder) mfrac
     expo   = foldMap expBuilder mexp
 
 -- | Returns a normalised 'Scientific' value or Nothing if the exponent
diff --git a/src/Waargonaut/Types/JObject.hs b/src/Waargonaut/Types/JObject.hs
--- a/src/Waargonaut/Types/JObject.hs
+++ b/src/Waargonaut/Types/JObject.hs
@@ -35,12 +35,11 @@
 
 import           Control.Applicative       ((<*), (<*>))
 import           Control.Category          (id, (.))
-import           Control.Lens              (AsEmpty (..), At (..), Index, 
+import           Control.Lens              (AsEmpty (..), At (..), Index,
                                             IxValue, Ixed (..), Lens', Prism',
-                                            Rewrapped, Wrapped (..), cons, 
-                                            isn't, iso, nearly, prism', re, to,
-                                            ( # ), (.~), (<&>), (^.), (^?),
-                                            _Wrapped)
+                                            Rewrapped, Wrapped (..), cons,
+                                            isn't, iso, nearly, prism', to,
+                                            ( # ), (.~), (<&>), (^.), _Wrapped)
 
 import           Control.Monad             (Monad)
 import           Data.Bifoldable           (Bifoldable (bifoldMap))
@@ -56,8 +55,8 @@
 import           Data.Text                 (Text)
 import           Data.Traversable          (Traversable, traverse)
 
-import           Data.ByteString.Builder   (Builder)
-import qualified Data.ByteString.Builder   as BB
+import           Data.Text.Lazy.Builder    (Builder)
+import qualified Data.Text.Lazy.Builder    as TB
 
 import qualified Data.Witherable           as W
 
@@ -133,7 +132,7 @@
 -- This function is analogus to the 'Data.Map.alterF' function.
 jAssocAlterF :: (Monoid ws, Functor f) => Text -> (Maybe a -> f (Maybe a)) -> Maybe (JAssoc ws a) -> f (Maybe (JAssoc ws a))
 jAssocAlterF k f mja = fmap g <$> f (_jsonAssocVal <$> mja) where
-  g v = maybe (JAssoc (textToJString k) mempty mempty v) (jsonAssocVal .~ v) mja
+  g v = maybe (JAssoc (_JStringText # k) mempty mempty v) (jsonAssocVal .~ v) mja
 
 -- | The representation of a JSON object.
 --
@@ -255,7 +254,7 @@
 
 -- Compare a 'Text' to the key for a 'JAssoc' value.
 textKeyMatch :: Text -> JAssoc ws a -> Bool
-textKeyMatch k = (== Just k) . (^? jsonAssocKey . re _JString)
+textKeyMatch k = (== k) . (^. jsonAssocKey . _JStringText)
 
 -- | Parse a single "key:value" pair
 parseJAssoc
@@ -275,7 +274,7 @@
   -> JAssoc ws a
   -> Builder
 jAssocBuilder ws aBuilder (JAssoc k ktws vpws v) =
-  jStringBuilder k <> ws ktws <> BB.charUtf8 ':' <> ws vpws <> aBuilder ws v
+  jStringBuilder k <> ws ktws <> TB.singleton ':' <> ws vpws <> aBuilder ws v
 
 -- |
 --
diff --git a/src/Waargonaut/Types/JString.hs b/src/Waargonaut/Types/JString.hs
--- a/src/Waargonaut/Types/JString.hs
+++ b/src/Waargonaut/Types/JString.hs
@@ -4,6 +4,7 @@
 {-# LANGUAGE FlexibleInstances     #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE NoImplicitPrelude     #-}
+{-# LANGUAGE RankNTypes            #-}
 {-# LANGUAGE TypeFamilies          #-}
 -- | Types and functions for handling JSON strings.
 module Waargonaut.Types.JString
@@ -13,45 +14,50 @@
   , JString' (..)
   , AsJString (..)
 
+  , _JStringText
+  , stringToJString
+
     -- * Parser / Builder
   , parseJString
   , jStringBuilder
-
-  , textToJString
   ) where
 
-import           Prelude                 (Eq, Ord, Show, String)
+import           Prelude                 (Eq, Ord, Show, String, foldr)
 
-import           Control.Applicative     ((*>), (<*))
-import           Control.Category        (id, (.))
-import           Control.Error.Util      (note)
-import           Control.Lens            (Prism', Rewrapped, Wrapped (..), iso,
-                                          preview, prism, ( # ), (^?))
+import           Control.Applicative        ((*>), (<*))
+import           Control.Category           (id, (.))
+import           Control.Error.Util         (note)
+import           Control.Lens               (Prism', Rewrapped, Wrapped (..),
+                                             iso, prism, review, ( # ), (^?))
 
-import           Data.Either             (Either (Right))
-import           Data.Foldable           (Foldable, foldMap)
-import           Data.Function           (($))
-import           Data.Functor            (Functor, (<$>))
-import           Data.Semigroup          ((<>))
-import           Data.Text               (Text)
-import qualified Data.Text               as Text
-import           Data.Traversable        (Traversable, traverse)
+import           Data.Bifunctor             (first)
+import           Data.Either                (Either (Right))
+import           Data.Foldable              (Foldable, foldMap)
+import           Data.Function              (const, ($))
+import           Data.Functor               (Functor, fmap, (<$>))
+import           Data.Semigroup             ((<>))
+import           Data.Text                  (Text)
+import qualified Data.Text                  as Text
+import qualified Data.Text.Encoding         as Text
+import           Data.Traversable           (Traversable, traverse)
 
-import           Data.Vector             (Vector)
-import qualified Data.Vector             as V
+import           Data.Vector                (Vector)
+import qualified Data.Vector                as V
 
-import           Data.Digit              (HeXDigit)
+import           Data.Digit                 (HeXDigit)
 
-import           Text.Parser.Char        (CharParsing, char)
-import           Text.Parser.Combinators (many)
+import           Text.Parser.Char           (CharParsing, char)
+import           Text.Parser.Combinators    (many)
 
-import           Data.ByteString         (ByteString)
-import qualified Data.ByteString.Builder as BB
-import qualified Data.ByteString.Char8   as BS8
+import           Data.Text.Lazy.Builder     (Builder)
+import qualified Data.Text.Lazy.Builder     as TB
 
-import           Waargonaut.Types.JChar  (JChar, jCharBuilder, parseJChar,
-                                          utf8CharToJChar, _JChar)
+import qualified Data.ByteString.Lazy.Char8 as BS8
 
+import           Waargonaut.Types.JChar     (JChar, jCharBuilderTextL,
+                                             parseJChar, utf8CharToJChar,
+                                             _JChar)
+
 -- $setup
 -- >>> :set -XOverloadedStrings
 -- >>> import Control.Lens ((#))
@@ -64,6 +70,7 @@
 -- >>> import Waargonaut.Decode.Error (DecodeError)
 -- >>> import Waargonaut.Types.Whitespace
 -- >>> import Waargonaut.Types.JChar
+-- >>> import Data.Text.Lazy.Builder (toLazyText)
 ----
 
 -- | A JSON string is a list of JSON acceptable characters, we use a newtype to
@@ -94,13 +101,19 @@
   _JString = prism (\(JString' cs) -> V.toList cs) (Right . JString' . V.fromList)
 
 instance AsJString String where
-  _JString = prism (\(JString' cx) -> V.toList $ (_JChar #) <$> cx) (\x -> JString' . V.fromList <$> traverse (note x . (^? _JChar)) x)
-
-instance AsJString Text where
-  _JString = prism (Text.pack . (_JString #)) (\x -> note x . preview _JString . Text.unpack $ x)
+  _JString = prism
+    (\(JString' cx) -> V.toList $ (_JChar #) <$> cx)
+    (\x -> JString' . V.fromList <$> traverse (note x . (^? _JChar)) x)
 
-instance AsJString ByteString where
-  _JString = prism (BS8.pack . (_JString #)) (\x -> note x . preview _JString . BS8.unpack $ x)
+-- | Prism between a 'JString' and 'Text'.
+--
+-- JSON strings a wider range of encodings than 'Text' and to be consistent with
+-- the 'Text' type, these invalid types are replaced with a placeholder value.
+--
+_JStringText :: Prism' JString Text
+_JStringText = prism
+  (JString' . V.fromList . fmap utf8CharToJChar . Text.unpack)
+  (\x -> first (const x) . Text.decodeUtf8' . BS8.toStrict . BS8.pack . review _JString $ x)
 
 -- | Parse a 'JString', storing escaped characters and any explicitly escaped
 -- character encodings '\uXXXX'.
@@ -133,39 +146,31 @@
 
 -- | Builder for a 'JString'.
 --
--- >>> BB.toLazyByteString $ jStringBuilder ((JString' V.empty) :: JString)
+-- >>> toLazyText $ jStringBuilder ((JString' V.empty) :: JString)
 -- "\"\""
 --
--- >>> BB.toLazyByteString $ jStringBuilder ((JString' $ V.fromList [UnescapedJChar (JCharUnescaped 'a'),UnescapedJChar (JCharUnescaped 'b'),UnescapedJChar (JCharUnescaped 'c')]) :: JString)
+-- >>> toLazyText $ jStringBuilder ((JString' $ V.fromList [UnescapedJChar (JCharUnescaped 'a'),UnescapedJChar (JCharUnescaped 'b'),UnescapedJChar (JCharUnescaped 'c')]) :: JString)
 -- "\"abc\""
 --
--- >>> BB.toLazyByteString $ jStringBuilder ((JString' $ V.fromList [UnescapedJChar (JCharUnescaped 'a'),EscapedJChar (WhiteSpace CarriageReturn),UnescapedJChar (JCharUnescaped 'b'),UnescapedJChar (JCharUnescaped 'c')]) :: JString)
+-- >>> toLazyText $ jStringBuilder ((JString' $ V.fromList [UnescapedJChar (JCharUnescaped 'a'),EscapedJChar (WhiteSpace CarriageReturn),UnescapedJChar (JCharUnescaped 'b'),UnescapedJChar (JCharUnescaped 'c')]) :: JString)
 -- "\"a\\rbc\""
 --
--- >>> BB.toLazyByteString $ jStringBuilder ((JString' $ V.fromList [UnescapedJChar (JCharUnescaped 'a'),EscapedJChar (WhiteSpace CarriageReturn),UnescapedJChar (JCharUnescaped 'b'),UnescapedJChar (JCharUnescaped 'c'),EscapedJChar (Hex (HexDigit4 HeXDigita HeXDigitb HeXDigit1 HeXDigit2)),EscapedJChar (WhiteSpace NewLine),UnescapedJChar (JCharUnescaped 'd'),UnescapedJChar (JCharUnescaped 'e'),UnescapedJChar (JCharUnescaped 'f'),EscapedJChar QuotationMark]) :: JString)
+-- >>> toLazyText $ jStringBuilder ((JString' $ V.fromList [UnescapedJChar (JCharUnescaped 'a'),EscapedJChar (WhiteSpace CarriageReturn),UnescapedJChar (JCharUnescaped 'b'),UnescapedJChar (JCharUnescaped 'c'),EscapedJChar (Hex (HexDigit4 HeXDigita HeXDigitb HeXDigit1 HeXDigit2)),EscapedJChar (WhiteSpace NewLine),UnescapedJChar (JCharUnescaped 'd'),UnescapedJChar (JCharUnescaped 'e'),UnescapedJChar (JCharUnescaped 'f'),EscapedJChar QuotationMark]) :: JString)
 -- "\"a\\rbc\\uab12\\ndef\\\"\""
 --
--- >>> BB.toLazyByteString $ jStringBuilder ((JString' $ V.singleton (UnescapedJChar (JCharUnescaped 'a'))) :: JString)
+-- >>> toLazyText $ jStringBuilder ((JString' $ V.singleton (UnescapedJChar (JCharUnescaped 'a'))) :: JString)
 -- "\"a\""
 --
--- >>> BB.toLazyByteString $ jStringBuilder (JString' $ V.singleton (EscapedJChar ReverseSolidus) :: JString)
+-- >>> toLazyText $ jStringBuilder (JString' $ V.singleton (EscapedJChar ReverseSolidus) :: JString)
 -- "\"\\\\\""
 --
 jStringBuilder
   :: JString
-  -> BB.Builder
+  -> Builder
 jStringBuilder (JString' jcs) =
-  BB.charUtf8 '\"' <> foldMap jCharBuilder jcs <> BB.charUtf8 '\"'
+  TB.singleton '\"' <> foldMap jCharBuilderTextL jcs <> TB.singleton '\"'
 
--- | Prism between a 'JString' and 'Text'.
---
--- JSON strings a wider range of encodings than 'Text' and to be consistent with
--- the 'Text' type, these invalid types are replaced with a placeholder value.
---
-textToJString :: Text -> JString
-textToJString = JString' . Text.foldr (V.cons . utf8CharToJChar) V.empty
+-- | Convert a 'String' to a 'JString'.
+stringToJString :: String -> JString
+stringToJString = JString' . foldr (V.cons . utf8CharToJChar) V.empty
 
--- _JStringText :: Prism' JString Text
--- _JStringText = prism
---   ()
---   (\j@(JString' v) -> note j $ Text.pack . V.toList <$> traverse jCharToUtf8Char v)
diff --git a/src/Waargonaut/Types/Json.hs b/src/Waargonaut/Types/Json.hs
--- a/src/Waargonaut/Types/Json.hs
+++ b/src/Waargonaut/Types/Json.hs
@@ -62,11 +62,12 @@
 import           Data.Traversable            (Traversable (..))
 import           Data.Tuple                  (uncurry)
 
-import           Data.ByteString.Builder     (Builder)
-import qualified Data.ByteString.Builder     as BB
 import           Data.Maybe                  (Maybe)
 import           Data.Text                   (Text)
 
+import           Data.Text.Lazy.Builder      (Builder)
+import qualified Data.Text.Lazy.Builder      as TB
+
 import           Text.Parser.Char            (CharParsing, text)
 
 import           Waargonaut.Types.JArray     (JArray (..), jArrayBuilder,
@@ -219,9 +220,9 @@
 jTypesBuilder
   :: (WS -> Builder)
   -> JType WS Json
-  -> BB.Builder
-jTypesBuilder s (JNull tws)     = BB.stringUtf8 "null"                          <> s tws
-jTypesBuilder s (JBool b tws)   = BB.stringUtf8 (if b then "true" else "false") <> s tws
+  -> Builder
+jTypesBuilder s (JNull tws)     = TB.fromText "null"                          <> s tws
+jTypesBuilder s (JBool b tws)   = TB.fromText (if b then "true" else "false") <> s tws
 jTypesBuilder s (JNum jn tws)   = jNumberBuilder jn                             <> s tws
 jTypesBuilder s (JStr js tws)   = jStringBuilder js                             <> s tws
 jTypesBuilder s (JArr js tws)   = jArrayBuilder s waargonautBuilder js          <> s tws
diff --git a/src/Waargonaut/Types/Whitespace.hs b/src/Waargonaut/Types/Whitespace.hs
--- a/src/Waargonaut/Types/Whitespace.hs
+++ b/src/Waargonaut/Types/Whitespace.hs
@@ -25,8 +25,8 @@
                                           mapped, nearly, over, prism, prism',
                                           to, uncons, (^.), _2, _Wrapped)
 
-import           Data.ByteString.Builder (Builder)
-import qualified Data.ByteString.Builder as BB
+import           Data.Text.Lazy.Builder  (Builder)
+import qualified Data.Text.Lazy.Builder  as TB
 
 import           Data.Vector             (Vector)
 import qualified Data.Vector             as V
@@ -171,11 +171,11 @@
 
 -- | Create a 'Data.ByteString.Builder' from a 'Whitespace'
 whitespaceBuilder :: Whitespace -> Builder
-whitespaceBuilder Space          = BB.charUtf8 ' '
-whitespaceBuilder HorizontalTab  = BB.charUtf8 '\t'
-whitespaceBuilder LineFeed       = BB.charUtf8 '\f'
-whitespaceBuilder CarriageReturn = BB.charUtf8 '\r'
-whitespaceBuilder NewLine        = BB.charUtf8 '\n'
+whitespaceBuilder Space          = TB.singleton ' '
+whitespaceBuilder HorizontalTab  = TB.singleton '\t'
+whitespaceBuilder LineFeed       = TB.singleton '\f'
+whitespaceBuilder CarriageReturn = TB.singleton '\r'
+whitespaceBuilder NewLine        = TB.singleton '\n'
 {-# INLINE whitespaceBuilder #-}
 
 -- | Reconstitute the given whitespace into its original form.
diff --git a/test/Decoder.hs b/test/Decoder.hs
--- a/test/Decoder.hs
+++ b/test/Decoder.hs
@@ -96,7 +96,7 @@
   r <- D.runDecode d parseBS (D.mkCursor j)
 
   Either.either
-    (\(e, _) -> assertBool "Incorrect Error - Expected KeyDecodeFailed" (e == D.KeyNotFound "bar") )
+    (\(e, _) -> assertBool "Incorrect Error - Expected KeyDecodeFailed" (e == D.KeyNotFound "bar"))
     (\_ -> assertFailure "Expected Error!")
     r
 
@@ -184,10 +184,9 @@
 decodeAltError = D.runDecode decodeEitherAlt parseBS (D.mkCursor "{\"foo\":33}")
   >>= Either.either
                  (\(_,h) -> assertBool "BranchFail error not found in history" $
-                 case Seq.viewr (unCursorHistory' h) of
-                   _ Seq.:> (BranchFail _, _) -> True
-                   _                          -> False
-
+                   case Seq.viewr (unCursorHistory' h) of
+                     _ Seq.:> (BranchFail _, _) -> True
+                     _                          -> False
                  )
                  (\_ -> assertFailure "Alt Error Test should fail")
 
diff --git a/test/Encoder.hs b/test/Encoder.hs
--- a/test/Encoder.hs
+++ b/test/Encoder.hs
@@ -17,7 +17,7 @@
 import           Waargonaut.Encode     (Encoder, Encoder')
 import qualified Waargonaut.Encode     as E
 
-import           Data.ByteString.Lazy  (ByteString)
+import           Data.Text.Lazy  (Text)
 
 import           Types.Common          (Image (..), Overlayed (..), testFudge,
                                         testImageDataType)
@@ -25,7 +25,7 @@
 import           Waargonaut.Generic    (GWaarg, mkEncoder, proxy)
 import           Waargonaut.Types.Json (oat)
 
-testImageEncodedNoSpaces :: ByteString
+testImageEncodedNoSpaces :: Text
 testImageEncodedNoSpaces = "{\"Width\":800,\"Height\":600,\"Title\":\"View from 15th Floor\",\"Animated\":false,\"IDs\":[116,943,234,38793]}"
 
 -- | The recommended way of defining an Encoder is to be explicit.
@@ -37,13 +37,13 @@
   . E.boolAt "Animated" (_imageAnimated img)
   . E.listAt E.int "IDs" (_imageIDs img)
 
-testFudgeEncodedWithConsName :: ByteString
+testFudgeEncodedWithConsName :: Text
 testFudgeEncodedWithConsName = "{\"fudgey\":\"Chocolate\"}"
 
 testOverlayed :: Overlayed
 testOverlayed = Overlayed "fred" testFudge
 
-testOverlayedOut :: ByteString
+testOverlayedOut :: Text
 testOverlayedOut = "{\"id\":\"fred\",\"fudgey\":\"Chocolate\"}"
 
 encodeOverlay :: Applicative f => Encoder f Overlayed
@@ -56,7 +56,7 @@
   :: TestName
   -> Encoder' a
   -> a
-  -> ByteString
+  -> Text
   -> TestTree
 tCase nm enc a expected = testCase nm $
   E.simplePureEncodeNoSpaces enc a @?= expected
diff --git a/test/Encoder/Laws.hs b/test/Encoder/Laws.hs
--- a/test/Encoder/Laws.hs
+++ b/test/Encoder/Laws.hs
@@ -5,9 +5,9 @@
 import           Test.Tasty                 (TestTree, testGroup)
 import           Test.Tasty.Hedgehog        (testProperty)
 
-import           Data.ByteString.Lazy       (ByteString)
 import           Data.Functor.Contravariant (contramap)
 import           Data.Functor.Identity      (Identity)
+import           Data.Text.Lazy             (Text)
 
 import           Hedgehog
 import qualified Hedgehog.Function          as Fn
@@ -18,7 +18,7 @@
 
 import qualified Laws
 
-runSE :: ShowEncoder a -> a -> ByteString
+runSE :: ShowEncoder a -> a -> Text
 runSE (SE e) = E.simplePureEncodeNoSpaces e
 
 newtype ShowEncoder a = SE (Encoder Identity a)
diff --git a/test/Laws.hs b/test/Laws.hs
--- a/test/Laws.hs
+++ b/test/Laws.hs
@@ -154,7 +154,7 @@
      ( Show a, Arg a, Vary a, Eq a
      , Show b, Arg b, Vary b
      , Show (f a), Eq (f a)
-     , Show (f a), Eq (f b)
+     , Eq (f b)
      , Applicative f
      )
   => (forall x. x -> f x)
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -2,6 +2,8 @@
 {-# LANGUAGE TupleSections     #-}
 module Main (main) where
 
+import           GHC.Word                    (Word8)
+
 import           Control.Lens                (( # ), (^.), (^?), _2)
 import qualified Control.Lens                as L
 import           Control.Monad               (when)
@@ -10,16 +12,19 @@
 
 import qualified Data.Scientific             as Sci
 
-import           Data.ByteString             (ByteString)
-import qualified Data.ByteString.Builder     as BB
-import qualified Data.ByteString.Char8       as BS8
-import qualified Data.ByteString.Lazy.Char8  as BSL8
-
+import Data.Functor.Contravariant ((>$<))
 import           Data.Maybe                  (fromMaybe)
 import           Data.Semigroup              ((<>))
 import           Data.Text                   (Text)
+import qualified Data.Text                   as Text
 import qualified Data.Text.Encoding          as Text
+import qualified Data.Text.IO                as Text
 
+import qualified Data.Text.Lazy              as TextL
+import qualified Data.Text.Lazy.Builder      as TB
+
+import qualified Data.ByteString             as BS
+
 import qualified Data.Sequence               as S
 
 import           Hedgehog
@@ -65,17 +70,9 @@
 encodeText
   :: Json
   -> Text
-encodeText =
-  Text.decodeUtf8 .
-  encodeByteString
-
-encodeByteString
-  :: Json
-  -> ByteString
-encodeByteString =
-  BSL8.toStrict .
-  BB.toLazyByteString .
-  W.waargonautBuilder WS.wsBuilder
+encodeText = TextL.toStrict
+  . TB.toLazyText
+  . W.waargonautBuilder WS.wsBuilder
 
 decode
   :: Text
@@ -162,12 +159,15 @@
     maxI :: Integer
     maxI = 2 ^ (32 :: Integer)
 
+simpleDecodeWith :: D.Decoder L.Identity a -> TextL.Text -> Either (DecodeError, D.CursorHistory) a
+simpleDecodeWith d = D.simpleDecode d Common.parseBS . Text.encodeUtf8 . TextL.toStrict
+
 prop_tripping_int_list :: Property
 prop_tripping_int_list = property $ do
   xs <- forAll . Gen.list (Range.linear 0 100) $ Gen.int (Range.linear 0 9999)
   tripping xs
     (E.simplePureEncodeNoSpaces (E.traversable E.int))
-    (D.simpleDecode (D.list D.int) Common.parseBS . BSL8.toStrict)
+    (simpleDecodeWith (D.list D.int))
 
 prop_tripping_image_record_generic :: Property
 prop_tripping_image_record_generic = withTests 1 . property $
@@ -210,7 +210,7 @@
   where
     trippin' a = tripping a
       (E.simplePureEncodeNoSpaces enc)
-      (D.simpleDecode dec Common.parseBS . BSL8.toStrict)
+      (simpleDecodeWith dec)
 
     enc = E.maybeOrNull' . E.mapLikeObj' . E.atKey' "boop"
       $ E.maybeOrNull' (E.mapLikeObj' (E.atKey' "beep" E.bool'))
@@ -241,11 +241,11 @@
   , testProperty "print . parse . print = print" prop_print_parse_print_id
   ]
 
-parsePrint :: ByteString -> Either DecodeError ByteString
-parsePrint = fmap encodeByteString . decode . Text.decodeUtf8
+parsePrint :: Text -> Either DecodeError Text
+parsePrint = fmap encodeText . decode
 
-readTestFile :: FilePath -> IO ByteString
-readTestFile fp = BS8.readFile ("test/json-data" <> "/" <> fp)
+readTestFile :: FilePath -> IO Text
+readTestFile fp = Text.readFile ("test/json-data" <> "/" <> fp)
 
 testFile :: FilePath -> Assertion
 testFile fp = do
@@ -272,6 +272,29 @@
       , "numbers.json"
       ]
 
+mishandlingOfCharVsUtf8Bytes :: TestTree
+mishandlingOfCharVsUtf8Bytes = testCaseSteps "Mishandling of UTF-8 Bytes vs Haskell Char" $ \step -> do
+  let
+    valChar      = '\128'          :: Char
+    valText      = "\128"          :: Text
+    valStr       = [valChar]        :: String
+    encVal       = "\"\128\""      :: Text
+    valUtf8Bytes = [34,194,128,34] :: [Word8]
+
+  step "Pack String to Text"
+  Text.pack valStr @?= valText
+
+  step "Encoder via Text"
+  x <- TextL.toStrict <$> E.simpleEncodeNoSpaces E.text valText
+  x @?= encVal
+
+  step "encoder output ~ packed bytes"
+  Text.encodeUtf8 x @?= BS.pack valUtf8Bytes
+
+  step "Decoder via Text"
+  y <- D.runDecode D.text Common.parseBS (D.mkCursor (Text.encodeUtf8 x))
+  y @?= Right valText
+
 regressionTests :: TestTree
 regressionTests =
   testGroup "Regression Tests - Failure to parse = Success" (toTestFail <$> fs)
@@ -290,10 +313,37 @@
   , tripping_properties
   , unitTests
   , regressionTests
+  , mishandlingOfCharVsUtf8Bytes
 
   , Decoder.decoderTests
   , Encoder.encoderTests
 
   , Decoder.Laws.decoderLaws
   , Encoder.Laws.encoderLaws
+
+  , testGroup "text gen - text e/d"
+    [ testProperty "unicode"       $ p Gen.text Gen.unicode E.text D.text
+    , testProperty "latin1"        $ p Gen.text Gen.latin1 E.text D.text
+    , testProperty "ascii"         $ p Gen.text Gen.ascii E.text D.text
+    ]
+  , testGroup "bytestring gen - via text e/d"
+    [ testProperty "unicode"       $ p Gen.utf8 Gen.unicode bsE bsD
+    , testProperty "latin1"        $ p Gen.utf8 Gen.latin1 bsE bsD
+    , testProperty "ascii"         $ p Gen.utf8 Gen.ascii bsE bsD
+    ]
   ]
+  where
+    bsE = Text.decodeUtf8 >$< E.text
+    bsD = Text.encodeUtf8 <$> D.text
+
+    p :: ( Eq a
+         , Show a
+         )
+      => (Range Int -> Gen Char -> Gen a)
+      -> Gen Char
+      -> E.Encoder' a
+      -> D.Decoder L.Identity a
+      -> Property
+    p f g e d = property $ do
+      inp <- forAll $ f (Range.linear 0 1000) g
+      tripping inp (E.simplePureEncodeNoSpaces e) (simpleDecodeWith d)
diff --git a/test/Types/Common.hs b/test/Types/Common.hs
--- a/test/Types/Common.hs
+++ b/test/Types/Common.hs
@@ -17,7 +17,6 @@
   , hexadecimalDigitLower
 
   , prop_generic_tripping
-  , parseWith
   , parseBS
   , parseText
 
@@ -37,7 +36,7 @@
 import           Generics.SOP                (Generic, HasDatatypeInfo)
 import qualified GHC.Generics                as GHC
 
-import           Control.Lens                (makeClassy, over, _Left)
+import           Control.Lens                (makeClassy)
 import           Control.Monad               ((>=>))
 
 import           Data.Functor.Identity       (Identity)
@@ -45,18 +44,19 @@
 import           Data.List.NonEmpty          (NonEmpty)
 import           Data.Maybe                  (fromMaybe)
 import           Data.Text                   (Text)
-import qualified Data.Text                   as Text
 
+import qualified Data.Text.Encoding          as Text
+
+import qualified Data.Text.Lazy              as TextL
+
 import           Hedgehog
 import qualified Hedgehog.Gen                as Gen
 import qualified Hedgehog.Range              as Range
 
 import           Data.ByteString             (ByteString)
-import qualified Data.ByteString.Lazy.Char8  as BSL8
 
 import qualified Data.Attoparsec.ByteString  as AB
 import qualified Data.Attoparsec.Text        as AT
-import           Data.Attoparsec.Types       (Parser)
 
 import           Data.Tagged                 (Tagged)
 import qualified Data.Tagged                 as T
@@ -69,7 +69,7 @@
 
 import qualified Waargonaut.Decode           as SD
 
-import           Waargonaut.Decode.Error     (DecodeError (ParseFailed))
+import           Waargonaut.Decode.Error     (DecodeError)
 import qualified Waargonaut.Encode           as E
 import           Waargonaut.Types            (Json)
 import           Waargonaut.Types.Whitespace (Whitespace (..))
@@ -252,14 +252,11 @@
 genText :: Gen Text
 genText = Gen.text ( Range.linear 0 100 ) Gen.unicodeAll
 
-parseWith :: (Parser t a -> t -> Either String a) -> Parser t a -> t -> Either DecodeError a
-parseWith f p = over _Left (ParseFailed . Text.pack . show) . f p
-
 parseBS :: ByteString -> Either DecodeError Json
-parseBS = parseWith AB.parseOnly parseWaargonaut
+parseBS = SD.parseWith AB.parseOnly parseWaargonaut
 
 parseText :: Text -> Either DecodeError Json
-parseText = parseWith AT.parseOnly parseWaargonaut
+parseText = SD.parseWith AT.parseOnly parseWaargonaut
 
 prop_generic_tripping
   :: ( MonadTest m
@@ -272,4 +269,4 @@
   -> m ()
 prop_generic_tripping e d a = tripping a
   (E.simplePureEncodeNoSpaces (T.untag e))
-  (SD.runPureDecode (T.untag d) parseBS . SD.mkCursor . BSL8.toStrict)
+  (SD.runPureDecode (T.untag d) parseBS . SD.mkCursor . Text.encodeUtf8 . TextL.toStrict)
diff --git a/test/Utils.hs b/test/Utils.hs
--- a/test/Utils.hs
+++ b/test/Utils.hs
@@ -12,16 +12,15 @@
 import           Data.Attoparsec.Text    (Parser, anyChar, endOfInput,
                                           parseOnly)
 
+import           Waargonaut.Decode       (parseWith)
 import           Waargonaut.Decode.Error (DecodeError)
 
-import           Types.Common            (parseWith)
-
 testparse
   :: Parser a
   -> Text
   -> Either DecodeError a
-testparse =
-  parseWith parseOnly
+testparse p =
+  parseWith parseOnly p
 
 testparsetheneof
   :: Parser a
diff --git a/waargonaut.cabal b/waargonaut.cabal
--- a/waargonaut.cabal
+++ b/waargonaut.cabal
@@ -10,7 +10,7 @@
 -- PVP summary:      +-+------- breaking API changes
 --                   | | +----- non-breaking API additions
 --                   | | | +--- code changes with no API change
-version:             0.4.2.0
+version:             0.5.0.0
 
 -- A short (one-line) description of the package.
 synopsis:            JSON wrangling
@@ -60,8 +60,8 @@
 tested-with:         GHC==7.10.3
                    , GHC==8.0.2
                    , GHC==8.2.2
-                   , GHC==8.4.3
-                   , GHC==8.6.1
+                   , GHC==8.4.4
+                   , GHC==8.6.2
 
 custom-setup
   setup-depends:       base >= 4 && <5
@@ -96,6 +96,7 @@
                      , Waargonaut.Encode
 
                      , Waargonaut.Generic
+                     , Waargonaut.Test
 
                      , Waargonaut
 
@@ -103,8 +104,8 @@
 
   -- Other library packages from which modules are imported.
   build-depends:       base                >= 4.7     && < 4.13
-                     , lens                >= 4.15    && < 5
-                     , mtl                 >= 2.2.2   && < 3
+                     , lens                >= 4.15    && < 4.18
+                     , mtl                 >= 2.2.2   && < 2.3
                      , text                >= 1.2     && < 1.3
                      , bytestring          >= 0.10.6  && < 0.11
                      , parsers             >= 0.12    && < 0.13
@@ -115,15 +116,15 @@
                      , nats                >= 1       && <1.2
                      , zippers             >= 0.2     && < 0.3
                      , vector              >= 0.12    && < 0.13
-                     , errors              >= 2.2     && < 3
+                     , errors              >= 2.2     && < 2.4
                      , hoist-error         >= 0.2     && < 0.3
                      , containers          >= 0.5.6   && < 0.7
-                     , witherable          >= 0.2     && < 0.3
+                     , witherable          >= 0.2     && < 0.4
                      , generics-sop        >= 0.3.2   && < 0.4
-                     , mmorph              >= 1.1     && < 2
+                     , mmorph              >= 1.1     && < 1.2
                      , transformers        >= 0.4     && < 0.6
-                     , bifunctors          >= 5       && < 6
-                     , contravariant       >= 1.4     && < 2
+                     , bifunctors          >= 5       && < 5.6
+                     , contravariant       >= 1.4     && < 1.6
                      , wl-pprint-annotated >= 0.1     && < 0.2
                      , hw-json             >= 0.9.0.1 && < 0.10
                      , hw-prim             >= 0.6     && < 0.7
@@ -131,8 +132,8 @@
                      , hw-rankselect       >= 0.10    && < 0.13
                      , hw-bits             >= 0.7     && < 0.8
                      , tagged              >= 0.8.6   && < 0.9
-                     , semigroupoids       >= 5.2.2   && < 6
-                     , natural             >= 0.3     && < 4
+                     , semigroupoids       >= 5.2.2   && < 5.4
+                     , natural             >= 0.3     && < 0.4
 
   -- Directories containing source files.
   hs-source-dirs:      src
@@ -180,13 +181,13 @@
   hs-source-dirs:      test
 
   build-depends:       base                   >= 4.7    && < 4.13
-                     , tasty                  >= 0.11   && < 2
+                     , tasty                  >= 0.11   && < 1.2
                      , tasty-hunit            >= 0.10   && < 0.11
                      , tasty-expected-failure >= 0.11   && < 0.12
                      , hedgehog               >= 0.6    && < 0.7
                      , hedgehog-fn            >= 0.6    && < 0.7
                      , tasty-hedgehog         >= 0.2    && < 0.3
-                     , lens                   >= 4.15   && < 5
+                     , lens                   >= 4.15   && < 4.18
                      , distributive           >= 0.5    && < 0.7
                      , bytestring             >= 0.10.6 && < 0.11
                      , digit                  >= 0.7    && < 0.8
@@ -198,11 +199,11 @@
                      , attoparsec             >= 0.13   && < 0.15
                      , scientific             >= 0.3    && < 0.4
                      , tagged                 >= 0.8.6  && < 0.9
-                     , mtl                    >= 2.2.2  && < 3
-                     , semigroupoids          >= 5.2.2  && < 6
+                     , mtl                    >= 2.2.2  && < 2.3
+                     , semigroupoids          >= 5.2.2  && < 5.6
                      , containers             >= 0.5.6  && < 0.7
-                     , natural                >= 0.3    && < 4
-                     , contravariant       >= 1.4     && < 2
+                     , natural                >= 0.3    && < 0.4
+                     , contravariant          >= 1.4    && < 1.6
 
                      , waargonaut
 
