json-autotype 3.0.5 → 3.1.0
raw patch · 6 files changed
+138/−17 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- README.md +1/−0
- changelog.md +4/−0
- json-autotype.cabal +3/−2
- src/Data/Aeson/AutoType/CodeGen/Haskell.hs +30/−14
- src/Data/Aeson/AutoType/Nested.hs +98/−0
- src/Data/Aeson/AutoType/Split.hs +2/−1
README.md view
@@ -144,3 +144,4 @@ * [PADS project](https://www.cs.princeton.edu/~dpw/papers/padsml06.pdf) is another attempt to automatically infer types to treat <em>arbitrary</em> data formats (not just JSON). It mixes type declarations, with parsing/printing information in order to have a consistent view of both. It does not handle automatic type inference though. * [JSON Schema generator](https://www.newtonsoft.com/jsonschema/help/html/GenerateSchema.htm) uses .NET types to generate JSON Schema instead (in opposite direction.) Similar schema generation is [used here](https://sixgun.wordpress.com/2012/02/09/using-json-net-to-generate-jsonschema/) * Microsoft Developer Network advocates use of [Data Contracts](https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/using-data-contracts) instead to constrain possible input data.+* [QuickType](https://github.com/quicktype/quicktype) uses [Markov chains heuristic](https://blog.quicktype.io/markov/) instead of theory
changelog.md view
@@ -1,5 +1,9 @@ Changelog =========+ 3.1.0 Apr 2020+ * New, experimental interface for nesting+ generated types by other code generators.+ 3.0.5 Mar 2020 * Using package import from `run-haskell-module` by default. * Expose commonly used functions for:
json-autotype.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: fb4adc71d7680a6ad92e8ac005c8302728f695d5a933e624e6b48ffb55d2787e+-- hash: 69c68aeb21aa0332a50089781cb19f9fb197bb8c6d7d633af4613de8aa4dd367 name: json-autotype-version: 3.0.5+version: 3.1.0 synopsis: Automatic type declaration for JSON input data description: Generates datatype declarations with Aeson''s ''Data.Aeson.FromJSON'' .@@ -95,6 +95,7 @@ Data.Aeson.AutoType.CodeGen.ElmFormat Data.Aeson.AutoType.CodeGen.Generic Data.Aeson.AutoType.CodeGen.Haskell+ Data.Aeson.AutoType.Nested Data.Aeson.AutoType.Plugin.Subtype Paths_json_autotype hs-source-dirs:
src/Data/Aeson/AutoType/CodeGen/Haskell.hs view
@@ -8,6 +8,9 @@ , runHaskellModule , runHaskellModuleStrict , defaultHaskellFilename+ , importedModules+ , requiredPackages+ , generateModuleImports ) where import qualified Data.Text as Text@@ -49,21 +52,34 @@ -- It was automatically generated by `json-autotype`. module |] <> capitalize moduleName <> [src| where+|] <> generateModuleImports importedModules -import System.Exit (exitFailure, exitSuccess)-import System.IO (stderr, hPutStrLn)-import qualified Data.ByteString.Lazy.Char8 as BSL-import System.Environment (getArgs)-import Control.Monad (forM_, mzero, join)-import Control.Applicative-import Data.Aeson.AutoType.Alternative-import Data.Aeson(eitherDecode, Value(..), FromJSON(..), ToJSON(..),- pairs,- (.:), (.:?), (.=), object)-import Data.Monoid((<>))-import Data.Text (Text)-import qualified GHC.Generics-|]+type ModuleImport = Text++generateModuleImports :: [ModuleImport] -> Text+generateModuleImports = Text.unlines+ . fmap ("import " <>)++-- | List of packages required by modules below.+-- Keep and maintain together.+requiredPackages :: [Text]+requiredPackages = ["aeson", "json-alt", "base", "bytestring", "text"]++-- | List of modules to import+importedModules :: [ModuleImport]+importedModules = [+ " System.Exit (exitFailure, exitSuccess)"+ , " System.IO (stderr, hPutStrLn)"+ , "qualified Data.ByteString.Lazy.Char8 as BSL"+ , " System.Environment (getArgs)"+ , " Control.Monad (forM_, mzero, join)"+ , " Control.Applicative"+ , " Data.Aeson.AutoType.Alternative"+ , " Data.Aeson(eitherDecode, Value(..), FromJSON(..), ToJSON(..),pairs,(.:), (.:?), (.=), object)"+ , " Data.Monoid((<>))"+ , " Data.Text (Text)"+ , "qualified GHC.Generics"+ ] epilogue :: Text -> Text epilogue toplevelName = [src|
+ src/Data/Aeson/AutoType/Nested.hs view
@@ -0,0 +1,98 @@+{-# language DeriveGeneric #-}+{-# language OverloadedStrings #-}+-- | Simple interface for using AutoType inference+-- in other code generators.+--+-- Simply takes a list of Aeson values,+-- and returns a type description.+--+-- For this type description,+-- we can use function to generate an entire new module.+--+-- Note that while we can put more code in the module,+-- it is recommended to avoid multiple automatically+-- generated types in order to avoid name conflicts.+module Data.Aeson.AutoType.Nested(+ defaultImportedModules+ , generateModuleImports+ , inferType+ , CodeFragment+ , TypeName+ , DeclaredType(..)+ ) where++import Data.Aeson+import Data.Aeson.AutoType.CodeGen.Haskell(generateModuleImports, requiredPackages, importedModules)+import Data.Aeson.AutoType.CodeGen.HaskellFormat(displaySplitTypes)+import Data.Aeson.AutoType.Extract(extractType, unifyTypes)+import Data.Aeson.AutoType.Split(splitTypeByLabel)+import Data.Default+import Data.Typeable+import Data.Text(Text)+import GHC.Generics++-- FIXME: general type to compose generated types+-- move to JSON Autotype as library interface?+-- * API Response Structures+type CodeFragment = Text+type TypeName = Text+type ImportedModule = Text+type PackageName = Text++-- | Type declaration and its requirements+-- Content to embed in an autogenerated module:+-- * name of the type to reference+-- * declarations to describe it+-- * module imports necessary for declarations+-- to work+data DeclaredType = DeclaredType+ {+ -- | Code fragment to be inserted in generated module+ typeCodeFragment :: CodeFragment+ -- | Toplevel type name to refer to+ , typeName :: TypeName+ -- | List of clauses to add to imports list+ , typeImportedModules :: [ImportedModule]+ -- | List of packages to add to generated package dependencies+ , typePackages :: [PackageName]+ }+ deriving+ ( Eq+ , Show+ , Generic+ , Typeable+ )+++instance Default DeclaredType where+ -- Minimal placeholder to use in case we cannot infer proper type+ def = DeclaredType {+ typeCodeFragment = ""+ , typeName = "Data.Aeson.Value"+ , typeImportedModules = ["qualified Data.Aeson"]+ , typePackages = ["aeson"]+ }++-- | List of modules imported for Autotyped declarations+defaultImportedModules = importedModules++-- | Given intended type name, and a list of+-- text fields with JSON, return+-- either an error, or an `EndpointResponse`+-- that allows to declare and use this type+-- in generated module.+inferType :: Text -> [Value] -> DeclaredType+inferType typeName [] = def+inferType typeName jsonValues =+ DeclaredType {+ typeImportedModules = defaultImportedModules+ , typeCodeFragment = displaySplitTypes splitTypeDescriptors+ , typeName = typeName+ , typePackages = requiredPackages+ }+ where+ valueTypes = map extractType jsonValues+ -- FIXME: should be <> in Typelike?+ unifiedType = foldr1 unifyTypes valueTypes+ splitTypeDescriptors = splitTypeByLabel typeName unifiedType+
src/Data/Aeson/AutoType/Split.hs view
@@ -62,7 +62,8 @@ splitTypeByLabel' _ TDouble = return TDouble splitTypeByLabel' _ TBool = return TBool splitTypeByLabel' _ TNull = return TNull-splitTypeByLabel' _ (TLabel r) = assert False $ return $ TLabel r -- unnecessary?+splitTypeByLabel' _ (TLabel r) = error $ "Splitting into labelled types after label "+ <> show r <> " was already given!" splitTypeByLabel' l (TUnion u) = do m <- mapM (splitTypeByLabel' l) $ Set.toList u return $! TUnion $! Set.fromList m splitTypeByLabel' l (TArray a) = do m <- splitTypeByLabel' (l `Text.append` "Elt") a