问题描述
如何将任意结构转换为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
我实际上想做的是在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)
由于在Dhall中看起来很困难,所以我决定采用这种方式:
data SInteger = SInteger { minimum :: Maybe Int,example :: Maybe Int }
data sstring = sstring { format :: Maybe String,length :: Maybe Int }
data Object = Object [(String,required)
integer :: SInteger -> Schema
string :: sstring -> Schema
object :: Object -> Schema
type Schema = JSON
但是在这条路上,我也被困住了。我愿意为不修补dhall-json
而牺牲一些类型的刚性。
解决方法
本指南概述了基本概念:
...,这就是您的示例上下文中的外观:
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,required : Bool } → Schema
= λ(x : SObject Schema) →
λ(Schema : Type) →
λ(schema : Constructors Schema) →
let Input = { name : Text,schema : Schema@1,required : Bool }
let Output = { name : Text,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 }