JSON to Swift Struct
Generate Codable Swift structs that JSONDecoder reads a JSON sample into and JSONEncoder writes back. Every value is typed, and a key that some objects lack is an Optional.
About JSON to Swift
The objects in the JSON become Swift structs that are Codable, which JSONDecoder reads the JSON into and JSONEncoder writes back. Properties are lowerCamelCase, initialisms in capitals, as avatarURL is for avatarUrl, and a struct with a property whose name is not its key has a CodingKeys enum, which gives each property its key.
Every value at a place counts, not just the first. A whole number is an Int, or an Int64, a UInt64 or a Decimal when the integers need one, since an Int is 32 bits on a 32-bit platform. A number written with a fraction or an exponent, as 10.0 and 1e3 are, makes a Double: JSONDecoder reads those into an Int too, but a key written so can hold 10.5 in the next response, which it refuses for an Int. JSONDecoder refuses a number beyond a Double, such as 1e400, for every number type, and a note says so.
The objects at one place, such as the items of a list, make one struct, with the keys in the order first seen. A key that is null in some objects, or that some objects lack, is an Optional, which JSONDecoder reads as nil for either, and which JSONEncoder leaves out when it is nil.
A key that holds two kinds of value, such as a string and a number, or only null, is a JSONValue, an enum at the end of the file that holds a value of any kind; objects that are always empty are a [String: JSONValue]. A note says why a property has each of those types, as one does for each number a Double, a Decimal or a JSONValue changes, and each null JSONEncoder leaves out.
A keyword is written in backticks, as `default` is, which JSONDecoder reads as default. A name already taken gets a number, as in userID2. Structs are UpperCamelCase, the struct of a list's items is singular, as Order is for orders, and none takes the name of a type of Swift or Foundation, which it would hide: data makes DataModel.
Swift reads keys by canonical equivalence, so é written as one character and as e and an accent is one key. A key that appears twice in an object is read once: Swift 6 reads its first value, and Swift 5.9 its last. JSONDecoder refuses half of a surrogate pair, which a Swift String cannot hold. A note names each such place in the JSON, and a key with half of a surrogate pair has no property.
When the JSON is an array of objects, the root struct describes them, and a note gives the type that reads the whole array. Choose Every field to make every property an Optional, for an API that can leave any key out. The code is a whole Swift file, which builds with no warning on Swift 5.9 and Swift 6.4.0, and needs only Foundation, which it imports. Everything runs in your browser.
For Kotlin, see JSON to Kotlin Data Class. For Rust, try JSON to Rust Struct, and for TypeScript, JSON to TypeScript.
Built and maintained by Meet Shah · Last updated
What this tool is used for
- Generating Codable structs from an API response, for an app on iOS or macOS, a server on Linux, or a test.
- Getting the property names and CodingKeys right for keys in snake_case, or with characters a Swift name cannot hold, such as spaces, dots and dashes.
- Writing the models for an endpoint with no OpenAPI schema, from a response copied out of the browser or a log.
- Seeing which keys only some objects of a list carry, as the Optional properties.
- Checking, from the notes, which numbers a
Double, aDecimalor aJSONValuewould change.
Frequently Asked Questions
- How do JSON types map to Swift?
- Strings are
String, booleansBool, objects a struct and arrays an array. A whole number is anInt, or anInt64,UInt64orDecimalwhen the integers need one, and a fraction or an exponent makes aDouble, as in 10.0 and 1e3. A key that holds two kinds of value, or only null, is aJSONValue. - Why is an integer an Int64 and not an Int?
Intis 32 bits on a 32-bit platform, where JSONDecoder refuses 2147483648 for it, so a key with an integer beyond 32 bits is anInt64, and a note says which integer. Beyond anInt64, it is aUInt64when no integer is below 0, and else aDecimal, which keeps 38 or 39 digits.- How are keys named?
- Each property is its key in lowerCamelCase, with initialisms in capitals, as
userIDis foruser_id. When a property's name is not its key, its struct has aCodingKeysenum, which lists every property with its key. A keyword, such as default, is written in backticks. - How are null and missing keys handled?
- A key that is null in some objects, or that some objects lack, is an Optional, which JSONDecoder reads as nil for either. JSONEncoder leaves a nil property out, so a null the JSON has for it is not written back, as a note says.
- What happens to a key no property reads?
- JSONDecoder skips it, and JSONEncoder does not write it back.
- Should every property be Optional?
- A sample shows only some of the keys an API can send as null or leave out: here a key that is null in some objects, or that some objects lack, is an Optional. The API's contract says which others are. Every field makes every property an Optional, so a response can leave any key out.
- What is the JSONValue at the end of the file?
- An enum that reads null, a
Bool, anInt64, aDouble, aString, an array or an object, the first that reads the value, and writes it back. It holds an object in a Dictionary, whose keys JSONEncoder writes in no fixed order without .sortedKeys. If another file of the module has a JSONValue too, keep one of them.
Common errors and gotchas
- Generating from a sample where a decimal field happens to hold a whole number, such as a price of 12: the property is an
Int, and JSONDecoder then refuses 12.5 for it. Use a sample with a fraction, or write 12.0, which gives aDouble. - Typing a key that some objects lack, or hold as null, as a plain
StringorInt: JSONDecoder throws a DecodingError for an object without the key, and for the null. Here such a key is an Optional. - Typing IDs as
Intbecause the sample's are small: on a 32-bit platform, JSONDecoder refuses 2147483648 for anInt. Here a key with an integer beyond 32 bits is anInt64. - Naming a property after a keyword:
default,classandselfdo not build. Here each is written in backticks, and JSONDecoder reads its key as it is. - Expecting a key that appears twice in an object to be read as JavaScript reads it: JSON.parse keeps the last value, as Swift 5.9's JSONDecoder does, but Swift 6's reads the first. A note names each place.
- Trusting the type a key has in one response: a number in one response can be a string in the next, which JSONDecoder refuses for an
Int. Paste several responses as an array, and every value counts.