RJson 0.3.5 → 0.3.6
raw patch · 3 files changed
+72/−35 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- README +3/−2
- RJson.cabal +1/−1
- Text/RJson.hs +68/−32
README view
@@ -1,8 +1,9 @@ ************************************************************************-* RJSon 0.3.5 *+* RJSon 0.3.6 * * Author: Alex Drummond * * *-* Thanks to Dustin DeWeese for a patch fixing a bug in the parser. *+* Thanks to Dustin DeWeese and Audrey Tang for patches fixing bugs in * +* the parser. * * Thanks to Adam Langley for a patch fixing the lack of support for * * null JSON values. * ************************************************************************
RJson.cabal view
@@ -1,5 +1,5 @@ Name: RJson-Version: 0.3.5+Version: 0.3.6 Cabal-Version: >= 1.2 License: BSD3 License-File: LICENSE
Text/RJson.hs view
@@ -60,11 +60,11 @@ -- | A Haskell representation of a JSON -- data structure.-data JsonData = JDString String |- JDNumber Double |- JDArray [JsonData] |- JDBool Bool |- JDNull |+data JsonData = JDString String |+ JDNumber Double |+ JDArray [JsonData] |+ JDBool Bool |+ JDNull | JDObject (M.Map String JsonData) listJoin :: a -> [a] -> [a]@@ -129,9 +129,9 @@ -- TranslateField class. -- class TranslateField a where- -- | By default, Haskell record field names are converted into- -- JSON object field names by stripping any initial underscores.- -- Specialize this method to define a different behavior.+ -- | This method defines the mapping from Haskell record field names+ -- to JSON object field names. The default is to strip any initial+ -- underscores. Specialize this method to define a different behavior. translateField :: a -> String -> String data TranslateFieldD a = TranslateFieldD { translateFieldD :: a -> String -> String }@@ -154,8 +154,8 @@ -- -- ToJson class plus SYB boilerplate. ----- | New instances can be added to this class to customize--- JSON serialization.+-- | New instances can be added to this class to customize certain aspects+-- of the way in which Haskell types are serialized to JSON. class TranslateField a => ToJson a where toJson :: a -> JsonData @@ -164,9 +164,10 @@ lToJson :: [a] -> JsonData lToJson l = JDArray (map toJson l) - -- | You can specialize this method to prevent fields from being serialized.- -- The method should return a list of the Haskell names of the fields to- -- be excluded.+ -- | Applies to record types only. You can specialize this method to+ -- prevent certain fields from being serialized.+ -- Given a Haskell field name, it should return True if that field is+ -- to be serialized, and False otherwise. exclude :: a -> String -> Bool exclude _ _ = False @@ -242,8 +243,11 @@ instance (ToJson a, TranslateField a, Typeable a, Typeable i, Ix i) => ToJson (Array i a) where toJson a = toJson (elems a) --- | Use this for merging two or more records together.--- Sensible instances of FromJson and ToJson are already defined for this type.+-- | This type can be used for merging two or more records together into a single+-- JSON object. By default, a structure such as (Union X Y) is serialized as follows.+-- First, X and Y are serialized, and a runtime error is signalled if the result of+-- serialization is not a JSON object in both cases. The key/value pairs of the+-- two JSON objects are then merged to form a single object. data Union a b = Union a b deriving Show $(derive[''Union]) -- In order to derive (Typeable2 Union). -- It seems that we get away with overwriting the instance@@ -279,22 +283,30 @@ typename x = dataTypeName (dataTypeOf toJsonProxy x) --- | This is the implementation of 'toJson' for the generic instance declaration,--- but it's useful to be able to use the same implentation for--- other instance declarations which override the default implementation--- of 'exclude'.+-- | This function is used as the the implementation of 'toJson' for the+-- generic instance declaration.+-- It's useful to be able to use the same implentation for+-- other instance declarations which override the default implementations+-- of other methods of the ToJson class. genericToJson :: (Data ToJsonD a, ToJson a, TranslateField a) => a -> JsonData genericToJson x- | isAlgType (dataTypeOf toJsonProxy x) =- case (map (translateFieldD' dict x) (filter (not . (excludeD dict x)) (getFields x))) of- [] -> case gmapQ toJsonProxy (toJsonD dict) x of- l -> JDArray $ (arrayPrependD dict x) ++ l ++ (arrayAppendD dict x)- fs -> JDObject (M.fromList (objectExtrasD dict x ++ (zip fs (gmapQ toJsonProxy (toJsonD dict) x))))- | True =- error ("Unable to serialize the primitive type '" ++ typename x ++ "'")+ | isAlgType (dataTypeOf toJsonProxy x) =+ case getFields x of+ [] ->+ case gmapQ toJsonProxy (toJsonD dict) x of+ [v] -> v -- Special default behavior for algebraic constructors with one field.+ vs -> JDArray $ (arrayPrependD dict x) ++ vs ++ (arrayAppendD dict x)+ fs ->+ let+ translatedFsToInclude =+ map (translateFieldD' dict x) (filter (not . (excludeD dict x)) (getFields x))+ in+ JDObject $ M.fromList (objectExtrasD dict x ++ (zip translatedFsToInclude (gmapQ toJsonProxy (toJsonD dict) x)))+ | True =+ error $ "Unable to serialize the primitive type '" ++ typename x ++ "'" -- | This function can be used as an implementation of 'toJson' for simple enums.--- It just converts an enum value to a string determined by the name of the constructor,+-- It converts an enum value to a string determined by the name of the constructor, -- after being fed through the (String -> String) function given as the first argument. enumToJson :: (Data ToJsonD a, ToJson a, TranslateField a) => (String -> String) -> a -> JsonData enumToJson transform x@@ -336,7 +348,7 @@ lFromJson :: a -> JsonData -> Either String [a] lFromJson dummy (JDArray l) = mapM (fromJson dummy) l - -- | In order to specify default values for required fields of a JSON object,+ -- | To specify default values for the required fields of a JSON object, -- specialize this method in the instance definition for the relevant -- datatype. objectDefaults :: a -> M.Map String JsonData@@ -517,6 +529,18 @@ Left e -> throwError e Right x -> return x)) +-- TODO: Another uninformative name.+m3 :: (Data FromJsonD a, TranslateField a) => JsonData -> a -> ErrorWithState String Int a+m3 jsondata dummy = do+ s <- get+ if s > 0+ then throwError "Bad fromJson conversion: Expecting JSON object or array; did not attempt automatic boxing because constructor takes more than one argument."+ else do+ put (s + 1)+ case fromJsonD dict dummy jsondata of+ Left e -> throwError e+ Right x -> return x+ genericFromJson :: (Data FromJsonD a, FromJson a, TranslateField a) => a -> JsonData -> Either String a genericFromJson dummy (JDArray l) = case datarep (dataTypeOf fromJsonProxy dummy) of@@ -529,12 +553,17 @@ AlgRep (c:_) -> case constrFields c of [] -> Left $ "Bad fromJson conversion: Attempt to convert JDObect to a non-record algebraic type"+ -- TODO: -- Can't use fromConstrM because we need to get dummy values of the -- appropriate type for each argument of the constructor. This is unfortunate,- -- since it means that we get runtime errors for records with strict fields.+ -- becuase it means that we get runtime errors for records with strict fields. fs -> evalState (runErrorT (gmapM fromJsonProxy (m2 (objectDefaultsD dict dummy) (translateFieldD'' dict dummy)) (fromConstr fromJsonProxy c))) (m, fs) AlgRep _ -> Left "Bad fromJson conversion: Type with no constructors!" _ -> Left "Bad fromJson conversion: Non-algebraic datatype given to 'genericFromJson'"+genericFromJson dummy jsondata =+ case datarep (dataTypeOf fromJsonProxy dummy) of+ AlgRep [c] -> evalState (runErrorT (gmapM fromJsonProxy (m3 jsondata) (fromConstr fromJsonProxy c))) 0+ AlgRep _ -> Left "Bad fromJson conversion: Expecting JSON object or array; did not attempt automatic boxing because type has more than one constructor." genericFromJson _ _ = Left "Bad fromJson conversion: Expecting JSON object or array" constrNames :: (Data FromJsonD a, Data TranslateFieldD a) => a -> [String]@@ -695,11 +724,17 @@ v <- jsonValue return (s, v) +lexeme :: P.Parser a -> P.Parser a+lexeme p = do+ r <- p+ ws+ return r+ jsonArray :: P.Parser JsonData jsonArray = do P.char '[' ws- vs <- P.sepBy jsonValue (ws >> P.char ',' >> ws)+ vs <- P.sepBy (lexeme jsonValue) (P.char ',' >> ws) ws P.char ']' return $ JDArray vs@@ -708,7 +743,7 @@ object = do P.char '{' ws- kvps <- P.sepBy kvp (ws >> P.char ',' >> ws)+ kvps <- P.sepBy (lexeme kvp) (P.char ',' >> ws) ws P.char '}' return $ JDObject $ M.fromList kvps@@ -756,11 +791,12 @@ -- -- A couple of utility functions. --+-- | Converts the first character of a string to upper case. firstCharToUpper :: String -> String firstCharToUpper "" = "" firstCharToUpper (c:cs) = (toUpper c) : cs +-- | Converts the first character of a string to lower case. firstCharToLower :: String -> String firstCharToLower "" = "" firstCharToLower (c:cs) = (toLower c) : cs-