Swift JSON Schema

CI

Swift JSON Schema 库提供了一种类型安全的方式,可以直接在 Swift 中生成和验证 JSON schema 文档。

请查看这个示例仓库,它使用 swift-json-schema 来创建类型安全的 OpenAI API 函数工具调用

Schema 生成

利用 Swift result builders 的强大功能来生成 JSON schema 文档。

@JSONSchemaBuilder var personSchema: some JSONSchemaComponent {
  JSONObject {
    JSONProperty(key: "firstName") {
      JSONString()
        .description("The person's first name.")
    }

    JSONProperty(key: "lastName") {
      JSONString()
        .description("The person's last name.")
    }

    JSONProperty(key: "age") {
      JSONInteger()
        .description("Age in years which must be equal to or greater than zero.")
        .minimum(0)
    }
  }
  .title("Person")
}
生成的 JSON Schema
{
  "$id": "https://example.com/person.schema.json",
  "$schema": "https://json-schema.fullstack.org.cn/draft/2020-12/schema",
  "title": "Person",
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string",
      "description": "The person's first name."
    },
    "lastName": {
      "type": "string",
      "description": "The person's last name."
    },
    "age": {
      "description": "Age in years which must be equal to or greater than zero.",
      "type": "integer",
      "minimum": 0
    }
  }
}

宏 (Macros)

使用来自 JSONSchemaBuilder@Schemable 宏来自动生成 result builders。

@Schemable
struct Person {
  let firstName: String
  let lastName: String?
  @NumberOptions(minimum: 0, maximum: 120)
  let age: Int
}
扩展后的宏 (Expanded Macro)
struct Person {
  let firstName: String
  let lastName: String?
  let age: Int

  // Auto-generated schema ↴
  static var schema: some JSONSchemaComponent<Person> {
    JSONSchema(Person.init) {
      JSONObject {
        JSONProperty(key: "firstName") {
          JSONString()
        }
        .required()
        JSONProperty(key: "lastName") {
          JSONString()
        }
        JSONProperty(key: "age") {
          JSONInteger()
          .minimum(0)
          .maximum(120)
        }
        .required()
      }
    }
  }
}
extension Person: Schemable {}

@Schemable 可以应用于枚举 (enums)。

@Schemable
enum Status {
  case active
  case inactive
}
扩展后的宏 (Expanded Macro)
enum Status {
  case active
  case inactive
  
  static var schema: some JSONSchemaComponent<Status> {
    JSONString()
      .enumValues {
        "active"
        "inactive"
      }
      .compactMap {
        switch $0 {
        case "active":
          return Self.active
        case "inactive":
          return Self.inactive
        default:
            return nil
        }
      }
    }
}
extension Status: Schemable {}

也支持使用 anyOf schema 组合的带有关联值的枚举。有关更多信息,请参阅 JSONSchemaBuilder 文档

验证 (Validation)

使用 Schema 类型,您可以针对 schema 验证 JSON 数据。

let schemaString = """
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1
    }
  }
}
"""
let schema1 = try Schema(instance: schemaString)
let result = try schema1.validate(instance: #"{"name": "Alice"}"#)

或者,您可以使用 JSONSchemaBuilder 构建器(或 )来创建 schema 并验证实例。

let nameBuilder = JSONObject {
  JSONProperty(key: "name") {
    JSONString()
      .minLength(1)
  }
}
let schema = nameBuilder.defintion()

let instance1: JSONValue = ["name": "Alice"]
let instance2: JSONValue = ["name": ""]

let result1 = schema.validate(instance1)
dump(result1, name: "Instance 1 Validation Result")
let result2 = schema.validate(instance2)
dump(result2, name: "Instance 2 Validation Result")
实例 1 验证结果
▿ Instance 1 Validation Result: JSONSchema.ValidationResult
- isValid: true
▿ keywordLocation: #
  - path: 0 elements
▿ instanceLocation: #
  - path: 0 elements
- errors: nil
▿ annotations: Optional([JSONSchema.Annotation<JSONSchema.Keywords.Properties>(keyword: "properties", instanceLocation: #, schemaLocation: #/properties, absoluteSchemaLocation: nil, value: Set(["name"]))])
  ▿ some: 1 element
    ▿ JSONSchema.Annotation<JSONSchema.Keywords.Properties>
      - keyword: "properties"
      ▿ instanceLocation: #
        - path: 0 elements
      ▿ schemaLocation: #/properties
        ▿ path: 1 element
          ▿ JSONSchema.JSONPointer.Component.key
            - key: "properties"
      - absoluteSchemaLocation: nil
      ▿ value: 1 member
        - "name"
实例 2 验证结果
▿ Instance 2 Validation Result: JSONSchema.ValidationResult
- isValid: false
▿ keywordLocation: #
  - path: 0 elements
▿ instanceLocation: #
  - path: 0 elements
▿ errors: Optional([JSONSchema.ValidationError(keyword: "properties", message: "Validation failed for keyword \'properties\'", keywordLocation: #/properties, instanceLocation: #, errors: Optional([JSONSchema.ValidationError(keyword: "minLength", message: "The string length is less than the specified \'minLength\'.", keywordLocation: #/properties/name/minLength, instanceLocation: #/name, errors: nil)]))])
  ▿ some: 1 element
    ▿ JSONSchema.ValidationError
      - keyword: "properties"
      - message: "Validation failed for keyword \'properties\'"
      ▿ keywordLocation: #/properties
        ▿ path: 1 element
          ▿ JSONSchema.JSONPointer.Component.key
            - key: "properties"
      ▿ instanceLocation: #
        - path: 0 elements
      ▿ errors: Optional([JSONSchema.ValidationError(keyword: "minLength", message: "The string length is less than the specified \'minLength\'.", keywordLocation: #/properties/name/minLength, instanceLocation: #/name, errors: nil)])
        ▿ some: 1 element
          ▿ JSONSchema.ValidationError
            - keyword: "minLength"
            - message: "The string length is less than the specified \'minLength\'."
            ▿ keywordLocation: #/properties/name/minLength
              ▿ path: 3 elements
                ▿ JSONSchema.JSONPointer.Component.key
                  - key: "properties"
                ▿ JSONSchema.JSONPointer.Component.key
                  - key: "name"
                ▿ JSONSchema.JSONPointer.Component.key
                  - key: "minLength"
            ▿ instanceLocation: #/name
              ▿ path: 1 element
                ▿ JSONSchema.JSONPointer.Component.key
                  - key: "name"
            - errors: nil
- annotations: nil

注意: 动态引用、词汇表和其他高级功能尚未支持。该库仍在开发中,将来会添加更多功能。 请参阅 当前不支持的测试,来自 JSON Schema 测试套件

解析 (Parsing)

当使用构建器时,您还可以将 JSON 实例解析为 Swift 类型。

@Schemable
enum TemperatureUnit {
  case celsius
  case fahrenheit
}

@Schemable
struct Weather {
  let temperature: Double
  let unit: TemperatureUnit
  let conditions: String
}

let data = """
{
  "temperature": 20,
  "unit": "celsius",
  "conditions": "Sunny"
}
"""
let weather: Parsed<Weather, ParseIssue> = Weather.schema.parse(instance: data)

可以选择将解析和验证合并为一个步骤。

let weather: Weather = try Weather.schema.parseAndValidate(instance: data)

感谢 swift-parsing 库和 Point-Free Parsing 系列,它们为解析 API 和实现提供了灵感。

文档 (Documentation)

该库的完整文档可通过 Swift Package Index 获得。

安装 (Installation)

您可以使用 Swift Package Manager (SPM) 或 Xcode 将 SwiftJSONSchema 包添加到您的项目中。

使用 Swift Package Manager (SPM)

要使用 Swift Package Manager 将 SwiftJSONSchema 添加到您的项目,请将以下依赖项添加到您的 Package.swift 文件中

dependencies: [
  .package(url: "https://github.com/ajevans99/swift-json-schema", from: "0.2.1")
]

然后,将 JSONSchema 和/或 JSONSchemaBuilder 作为目标的依赖项包含进去

targets: [
  .target(
    name: "YourTarget",
    dependencies: [
      .product(name: "JSONSchema", package: "swift-json-schema"),
      .product(name: "JSONSchemaBuilder", package: "swift-json-schema"),
    ]
  )
]

使用 Xcode

  1. 在 Xcode 中打开您的项目。
  2. 导航到 File > Swift Packages > Add Package Dependency...
  3. 输入存储库 URL:https://github.com/ajevans99/swift-json-schema
  4. 按照提示将该包添加到您的项目中。

添加后,您可以在您的 Swift 文件中导入 JSONSchema 并开始在您的项目中使用它。

许可证 (License)

该库是在 MIT 许可证下发布的。 有关详细信息,请参阅 LICENSE