NV ToolkitFormat · Validate · Convert

Code Generators

JSON to Swift Converter

Paste a JSON or XML sample — the format is auto-detected — and get typed Swift models with names, nesting and nullability inferred for you.

In short

Convert JSON to Swift online: paste a sample and generate typed Swift models with serialization helpers — powered by quicktype, in your browser.

By NaveenKumar T · Updated

Learn: JSON Examples: From Hello World to Real API Payloads

  • JSON or XML input, auto-detected
  • Nested objects become types
  • Nullability inferred

Runs entirely in your browser — nothing you paste or open here is uploaded, logged, or stored.How we handle data →

Example

Example: JSON order payload to Swift

A small order document with a nested object, an array of two items, a decimal total and a null field — enough to show how each Swift type, list and nullable is inferred. The same page accepts the XML form of this order too, so the sample below is an example rather than the only accepted format. The right-hand side is the exact output of this tool.

Input · JSON
{
  "id": "ord_9F2KX",
  "status": "processing",
  "paid": true,
  "customer": { "name": "Ada Lovelace", "email": "ada@example.com", "phone": null },
  "items": [
    { "sku": "KB-75", "qty": 1, "price": 149.99 },
    { "sku": "MS-12", "qty": 2, "price": 39.5 }
  ],
  "total": 228.99
}
Generated · Swift
// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
//   let root = try Root(json)

import Foundation

// MARK: - Root
struct Root: Codable {
    let id, status: String
    let paid: Bool
    let customer: Customer
    let items: [Item]
    let total: Double
}

// MARK: Root convenience initializers and mutators

extension Root {
    init(data: Data) throws {
        self = try newJSONDecoder().decode(Root.self, from: data)
    }

    init(_ json: String, using encoding: String.Encoding = .utf8) throws {
        guard let data = json.data(using: encoding) else {
            throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
        }
        try self.init(data: data)
    }

    init(fromURL url: URL) throws {
        try self.init(data: try Data(contentsOf: url))
    }

    func with(
        id: String? = nil,
        status: String? = nil,
        paid: Bool? = nil,
        customer: Customer? = nil,
        items: [Item]? = nil,
        total: Double? = nil
    ) -> Root {
        return Root(
            id: id ?? self.id,
            status: status ?? self.status,
            paid: paid ?? self.paid,
            customer: customer ?? self.customer,
            items: items ?? self.items,
            total: total ?? self.total
        )
    }

    func jsonData() throws -> Data {
        return try newJSONEncoder().encode(self)
    }

    func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
        return String(data: try self.jsonData(), encoding: encoding)
    }
}

// MARK: - Customer
struct Customer: Codable {
    let name, email: String
    let phone: JSONNull?
}

// MARK: Customer convenience initializers and mutators

extension Customer {
    init(data: Data) throws {
        self = try newJSONDecoder().decode(Customer.self, from: data)
    }

    init(_ json: String, using encoding: String.Encoding = .utf8) throws {
        guard let data = json.data(using: encoding) else {
            throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
        }
        try self.init(data: data)
    }

    init(fromURL url: URL) throws {
        try self.init(data: try Data(contentsOf: url))
    }

    func with(
        name: String? = nil,
        email: String? = nil,
        phone: JSONNull?? = nil
    ) -> Customer {
        return Customer(
            name: name ?? self.name,
            email: email ?? self.email,
            phone: phone ?? self.phone
        )
    }

    func jsonData() throws -> Data {
        return try newJSONEncoder().encode(self)
    }

    func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
        return String(data: try self.jsonData(), encoding: encoding)
    }
}

// MARK: - Item
struct Item: Codable {
    let sku: String
    let qty: Int
    let price: Double
}

// MARK: Item convenience initializers and mutators

extension Item {
    init(data: Data) throws {
        self = try newJSONDecoder().decode(Item.self, from: data)
    }

    init(_ json: String, using encoding: String.Encoding = .utf8) throws {
        guard let data = json.data(using: encoding) else {
            throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
        }
        try self.init(data: data)
    }

    init(fromURL url: URL) throws {
        try self.init(data: try Data(contentsOf: url))
    }

    func with(
        sku: String? = nil,
        qty: Int? = nil,
        price: Double? = nil
    ) -> Item {
        return Item(
            sku: sku ?? self.sku,
            qty: qty ?? self.qty,
            price: price ?? self.price
        )
    }

    func jsonData() throws -> Data {
        return try newJSONEncoder().encode(self)
    }

    func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
        return String(data: try self.jsonData(), encoding: encoding)
    }
}

// MARK: - Helper functions for creating encoders and decoders

func newJSONDecoder() -> JSONDecoder {
    let decoder = JSONDecoder()
    if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
        decoder.dateDecodingStrategy = .iso8601
    }
    return decoder
}

func newJSONEncoder() -> JSONEncoder {
    let encoder = JSONEncoder()
    if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
        encoder.dateEncodingStrategy = .iso8601
    }
    return encoder
}

// MARK: - Encode/decode helpers

class JSONNull: Codable, Hashable {

    public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
            return true
    }

    public var hashValue: Int {
            return 0
    }

    public init() {}

    public required init(from decoder: Decoder) throws {
            let container = try decoder.singleValueContainer()
            if !container.decodeNil() {
                    throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
            }
    }

    public func encode(to encoder: Encoder) throws {
            var container = encoder.singleValueContainer()
            try container.encodeNil()
    }
}

Look for the "phone" field: sampled as null, it is typed as JSONNull?. "items" became [Item], and "total" is Double.

Learn more

From JSON sample to Swift types

Hand-writing model classes from a JSON payload is transcription work: read a field, type a property, repeat eighty times, miss one. This page runs quicktype (the same engine behind the best-known generators) against your document, merging array elements to find the common shape and naming nested types from their keys.

JSONDecoder fails the whole decode the moment one key is missing, and the thrown error names the key path rather than a line number. Make fields the API can omit optional, or supply a sample that omits them so quicktype marks them optional for you; and remember Double is used for decimals, so money fields want a custom Decimal initializer rather than the generated type as-is.

How JSON values become Swift types

Inference works from the values in your sample. Strings map to String, integers and decimals are told apart (228.99 becomes Double, while 1 and 2 stay integral), true/false become the language's boolean, and an object such as "customer" becomes its own named type. The "items" array is typed as [Item] because every element shares the same keys — quicktype unifies the shapes of all array elements into one Item type.

Nulls are the one place inference needs your help: "phone": null carries no type information, so the field is emitted as JSONNull?. If the real API sometimes sends a string there, include a sample where the field is populated (paste several documents merged into one array) and the generated type becomes the union of what it saw.

Pasting XML instead: how elements and attributes become Swift types

The XML is parsed into a data model before inference (attributes included, text nodes named "text"), so the root element becomes a nested type: <order> is a field of type Order on the top-level Root rather than the top level itself. Child elements become fields, and repeated elements — the two <item> siblings — are collected into [Item] under an Items wrapper type that mirrors the <items> container.

Attributes and element text are typed differently, and this matters: element text is type-inferred, so <total>228.99</total> becomes Double, while attribute values are kept as they were written — qty="1" and price="149.99" stay String in Swift. Prefer element text for numeric data you want typed, or cast attribute values after decoding. One more trap: a single <item> would not be recognized as a list, so make sure repeated elements appear at least twice in the sample.

Annotations, nulls and nested arrays in the generated Swift

Swift needs no attributes: the structs conform to Codable and any mapping is handled by a generated CodingKeys enum — but only when a name has to change, as in case orderID = "order_id", with untouched names collapsed onto a single case line. When every key is already a valid lowerCamelCase identifier, no CodingKeys enum is emitted at all, which is why a tidy sample produces such short structs. Each type also gains convenience initializers from Data, String and URL, a with(...) copy helper, and jsonData/jsonString encoders built on a shared JSONDecoder factory.

A field whose only sampled value is null is typed JSONNull? and backed by a generated JSONNull class that encodes and decodes a literal null — accurate, but useless as a model. Paste a sample where the field carries a value and it becomes a plain String?, which is what belongs in a Codable struct. Fields absent from one merged sample also become optionals, so JSONDecoder stops throwing keyNotFound. An empty array falls back to an array of JSONAny, quicktype's type-erased box for values it could not infer.

Nested objects become sibling structs named after the key — Customer, Item — each Codable and each with its own convenience initializers and with(...) mutator, so you can decode a fragment on its own. Arrays of objects are typed as a Swift array of Item once quicktype has unified the elements. Small arrays are where a sample lets you down: a single element names the type but hides optionality, and an empty array produces an array of JSONAny with no Item struct generated to decode into.

Help

Frequently asked questions

Everything you need to know about the JSON to Swift.

01How do I generate Swift classes from JSON?

Paste a representative document — JSON or XML, the format is detected automatically — and press Convert. A real API response works best. Nested objects become their own Swift types, arrays become [Item], and the result downloads as Root.swift.

02What does the generated Swift code include?

Swift models generate as Codable structs with CodingKeys where names need mapping — paste an API response, get the types your URLSession decoding layer needs, snake_case handled.

03How do I use the generated Swift code in my project?

Add the file to your target and call let root = try Root(json) — a convenience initializer wrapping JSONDecoder — or decode directly with JSONDecoder().decode(Root.self, from: data). Each struct also gets jsonData() and jsonString() encoders and a with(...) helper for copying with changed fields, so the models stay immutable.

04Are the generated Swift types classes or structs?

Structs with Codable conformance — the idiomatic Swift choice for data models. JSONDecoder usage helpers are included, so the round-trip from Data to typed model is one call.

Keep working

Related tools

Learn the format

Related guides

All guides →