ajevans99 / swift-json-schema

Generate JSON Schema documents from Swift
MIT License
8 stars 2 forks source link

Swift JSON Schema

CI

JSON Schema is a powerful tool for defining the structure of JSON documents. Swift JSON Schema aims to make it easier to generate JSON schema documents directly in Swift.

Schema Generation

Swift JSON Schema enables type-safe JSON schema generation directly in Swift.

Here's a simple example of a person schema.

{
  "$id": "https://example.com/person.schema.json",
  "$schema": "https://json-schema.org/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
    }
  }
}

Let's create this schema directly in Swift. RootSchema and Schema types are used to define the schema structure. The RootSchema type represents the root of the schema document, and the Schema type represents a JSON schema object.

let schema = RootSchema(
  id: "https://example.com/person.schema.json",
  schema: "https://json-schema.org/draft/2020-12/schema",
  subschema: .object(
    .annotations(title: "Person"),
    .options(
      properties: [
        "firstName": .string(
          .annotations(description: "The person's first name.")
        ),
        "lastName": .string(
          .annotations(description: "The person's last name.")
        ),
        "age": .integer(
          .annotations(description: "Age in years which must be equal to or greater than zero."),
          .options(minimum: 0)
        )
      ]
    )
  )
)

Both Schema and RootSchema conform to Codable for easy serialization

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try encoder.encode(self)
let string = String(decoding: data, as: UTF8.self)

or deserialization

let decoder = JSONDecoder()
let data = Data(json.utf8)
let schema = try decoder.decode(Schema.self, from: data)

Result Builers

Import the JSONSchemaBuilder target and improve schema generation ergonomics with Swift's result builders.

@JSONSchemaBuilder var jsonSchema: 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")
}

let schema: Schema = jsonSchema.defintion

Macros

Use the @Schemable macro from JSONSchemaBuilder to automatically expand Swift types into JSON schema components.

@Schemable
struct Person {
  let firstName: String
  let lastName: String?
  @NumberOptions(minimum: 0, maximum: 120)
  let age: Int
}

The @Schemable attribute automatically expands the Person type into a JSON schema component.

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 {}

Additionally, @Schemable can be applied to Swift enums.

@Schemable
enum Status {
  case active
  case inactive

  // Auto-generated schema ↴
  static var schema: JSONSchemaComponent {
    JSONEnum {
      "active"
      "inactive"
    }
  }
}

Enums with associated values are also supported using anyOf schema composition. See the JSONSchemaBuilder documentation for more information.

Documentation

The full documentation for this library is available through the Swift Package Index.

Installation

You can add the SwiftJSONSchema package to your project using Swift Package Manager (SPM) or Xcode.

Using Swift Package Manager (SPM)

To add SwiftJSONSchema to your project using Swift Package Manager, add the following dependency to your Package.swift file:

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

Then, include JSONSchema and/or JSONSchemaBuilder as a dependency for your target:

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

Using Xcode

  1. Open your project in Xcode.
  2. Navigate to File > Swift Packages > Add Package Dependency...
  3. Enter the repository URL: https://github.com/ajevans99/swift-json-schema
  4. Follow the prompts to add the package to your project.

Once added, you can import JSONSchema in your Swift files and start using it in your project.

Next Steps

This library is in active development. If you have any feedback or suggestions, please open an issue or pull request.

Goals for future releases include:

License

This library is released under the MIT license. See LICENSE for details.