1

如何将任意结构转换为 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)

因为在 Dhall 看起来很难,所以我决定走这条路:

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

4

1 回答 1

0

本指南概述了基本思想:

......这是在您的示例上下文中的外观:

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 }
于 2020-08-30T16:37:38.220 回答