如何在dhall中将结构转换为json?

问题描述

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