如何将任意结构转换为json?
let Prelude = ./include/Prelude.dhall
let JSON = Prelude.JSON
let Foo = { a: Natural, t: Text }
let foo = { a = 10, b = "foo" }
in (DO_MAGIC foo) : JSON.Type
我知道有toMap
内置函数,但它需要一个同构的记录。
我实际想做的是用dhall编写OpenAPI规范。它的大多数部分都很简单,但描述传入数据形状的json模式是递归的,这在Dhall中很难做到。我想要的东西会像一样用Haskell表达
data Schema
= SInteger { minimum :: Maybe Int, example :: Maybe Int }
| SString { format :: Maybe String, length :: Maybe Int }
| Object [(String, Schema, Bool)] -- (name, schema, required)
deriving (ToJSON)
由于在达尔看起来很艰难,我决定走这条路:
data SInteger = SInteger { minimum :: Maybe Int, example :: Maybe Int }
data SString = SString { format :: Maybe String, length :: Maybe Int }
data Object = Object [(String, Schema, Bool)] -- (name, schema, required)
integer :: SInteger -> Schema
string :: SString -> Schema
object :: Object -> Schema
type Schema = JSON
但在这条路上我也被困了。我愿意牺牲一些类型的刚性来不修补dhall-json
。
本指南概述了基本思想:
- 如何将递归代码转换为Dhall
…下面是您的示例中的情况:
let List/map = https://prelude.dhall-lang.org/v17.1.0/List/map.dhall
let JSON = https://prelude.dhall-lang.org/v17.1.0/JSON/Type
let JSON/render = https://prelude.dhall-lang.org/v17.1.0/JSON/render
let SInteger = { minimum : Optional Integer, example : Optional Integer }
let SString = { format : Optional Text, length : Optional Natural }
let SObject =
λ(Schema : Type) → List { name : Text, schema : Schema, required : Bool }
let Constructors =
λ(Schema : Type) →
{ Integer : SInteger → Schema
, String : SString → Schema
, Object : SObject Schema → Schema
}
let Schema
: Type
= ∀(Schema : Type) → ∀(schema : Constructors Schema) → Schema
let integer
: SInteger → Schema
= λ(x : SInteger) →
λ(Schema : Type) →
λ(schema : Constructors Schema) →
schema.Integer x
let string
: SString → Schema
= λ(x : SString) →
λ(Schema : Type) →
λ(schema : Constructors Schema) →
schema.String x
let object
: List { name : Text, schema : Schema, required : Bool } → Schema
= λ(x : SObject Schema) →
λ(Schema : Type) →
λ(schema : Constructors Schema) →
let Input = { name : Text, schema : Schema@1, required : Bool }
let Output = { name : Text, schema : Schema, required : Bool }
let adapt =
λ(y : Input) →
{ schema = y.schema Schema schema } ∧ y.{ name, required }
in schema.Object (List/map Input Output adapt x)
let toJSON
: Schema → JSON
= λ(schema : Schema) →
λ(JSON : Type) →
λ ( json
: { array : List JSON → JSON
, bool : Bool → JSON
, double : Double → JSON
, integer : Integer → JSON
, null : JSON
, object : List { mapKey : Text, mapValue : JSON } → JSON
, string : Text → JSON
}
) →
schema
JSON
{ Integer =
λ(x : SInteger) →
json.object
( toMap
{ minimum =
merge
{ None = json.null, Some = json.integer }
x.minimum
, example =
merge
{ None = json.null, Some = json.integer }
x.example
}
)
, String =
λ(x : SString) →
json.object
( toMap
{ format =
merge
{ None = json.null, Some = json.string }
x.format
, length =
merge
{ None = json.null
, Some =
λ(n : Natural) →
json.integer (Natural/toInteger n)
}
x.length
}
)
, Object =
λ(x : SObject JSON) →
let Input = { name : Text, schema : JSON, required : Bool }
let Output = { mapKey : Text, mapValue : JSON }
let adapt =
λ(y : Input) →
{ mapKey = y.name
, mapValue =
json.object
( toMap
{ schema = y.schema
, required = json.bool y.required
}
)
}
in json.object (List/map Input Output adapt x)
}
let example =
let input =
object
[ { name = "foo"
, required = True
, schema = string { format = None Text, length = Some 10 }
}
, { name = "bar"
, required = False
, schema = integer { minimum = Some +0, example = Some +10 }
}
]
let output =
''
{
"foo": {
"required": true,
"schema": {
"format": null,
"length": 10
}
},
"bar": {
"required": false,
"schema": {
"example": 10,
"minimum": 0
}
}
}
''
in assert : JSON/render (toJSON input) ≡ output
in { Schema, integer, string, object, toJSON }