如何在OpenAPI/Haskell中展示完整的Schema定义?
如何生成包含所有依赖定义的完整JSON Schema(基于openapi3库)
我用openapi3库将Haskell的代数数据类型(ADT)转换为JSON Schema,相关代码如下:
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TemplateHaskell #-} module JSchema (Size) where import Control.Lens import Data.Proxy import Data.OpenApi import Types import Deal import GHC.Generics (Generic) import Language.Haskell.TH import Data.Aeson.TH import Data.Aeson.Types import Data.Ratio ((%)) import Data.Fixed data Unit = Inch | Meter deriving (Show, Generic) data Size = Balance Unit Int | Balance2 Float | Size deriving (Show, Generic) instance ToSchema Size $(deriveJSON defaultOptions ''Size) instance ToSchema Unit $(deriveJSON defaultOptions ''Unit)
当执行encodePretty $ toSchema $ (Proxy :: Proxy Size)生成Size的JSON Schema时,返回结果里只生成了指向Unit的$ref,没有包含Unit的具体定义。我需要给调用者提供完整的Schema,让他们能构造合法的JSON请求,但用toInlinedSchema会因为递归类型导致内存溢出,生成的Schema片段如下:
{ "oneOf": [ { "type": "object", "properties": { "contents": { "items": [ { "$ref": "#/components/schemas/Unit" // 问题所在 }, { "maximum": 9223372036854775807, "type": "integer", "minimum": -9223372036854775808 } ], "maxItems": 2, "type": "array", "minItems": 2 }, "tag": { "type": "string", "enum": [ "Balance" ] } } } // 其他分支省略 ] }
请问怎么生成包含所有依赖定义的完整Schema?
解决方法
不要直接生成单个Schema,而是构建完整的OpenAPI文档对象,将所有需要的Schema(包括Size和Unit)添加到components/schemas中,这样生成的文档会包含所有引用的定义,同时避免递归内联的问题。
具体实现代码
修改代码,构建完整的OpenApi对象:
import Data.Aeson.Encode.Pretty (encodePretty) main :: IO () main = do let sizeSchema = toSchema (Proxy :: Proxy Size) unitSchema = toSchema (Proxy :: Proxy Unit) -- 构建OpenAPI文档,添加schemas到components openApi = mempty & components . schemas . at "Size" ?~ sizeSchema & components . schemas . at "Unit" ?~ unitSchema -- 输出完整的OpenAPI文档 print $ encodePretty openApi
说明
OpenApi对象是openapi3库的核心结构,包含了所有API相关的元数据,其中components/schemas用于存放可复用的Schema定义。- 通过
lens库的操作符,将Size和Unit的Schema分别添加到schemas映射中,这样生成的JSON里,Size中的$ref就能正确指向#/components/schemas/Unit,同时Unit的定义也会出现在components/schemas下。 - 这种方式既保留了引用的结构(避免递归溢出),又能提供完整的定义给调用者。
如果只需要Schema部分而不是完整的OpenAPI文档,可以从生成的OpenApi对象中提取components.schemas部分输出:
main :: IO () main = do let sizeSchema = toSchema (Proxy :: Proxy Size) unitSchema = toSchema (Proxy :: Proxy Unit) schemas = mempty & at "Size" ?~ sizeSchema & at "Unit" ?~ unitSchema print $ encodePretty schemas
内容的提问来源于stack exchange,提问作者Shawn Zhang
相关产品推荐
相关产品推荐

