Code Generators
JSON to Objective-C Converter
Paste a JSON or XML sample — the format is auto-detected — and get typed Objective-C models with names, nesting and nullability inferred for you.
In short
Convert JSON to Objective-C online: paste a sample, get typed Objective-C models — quicktype-powered, free and in your browser.
By NaveenKumar T · Updated
- 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 Objective-C
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 Objective-C 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.
{
"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
}// QTRoot.h
// To parse this JSON:
//
// NSError *error;
// QTRoot *root = [QTRoot fromJSON:json encoding:NSUTF8Encoding error:&error];
#import <Foundation/Foundation.h>
@class QTRoot;
@class QTCustomer;
@class QTItem;
NS_ASSUME_NONNULL_BEGIN
#pragma mark - Object interfaces
@interface QTRoot : NSObject
@property (nonatomic, copy) NSString *identifier;
@property (nonatomic, copy) NSString *status;
@property (nonatomic, assign) BOOL isPaid;
@property (nonatomic, strong) QTCustomer *customer;
@property (nonatomic, copy) NSArray<QTItem *> *items;
@property (nonatomic, assign) double total;
+ (_Nullable instancetype)fromJSON:(NSString *)json encoding:(NSStringEncoding)encoding error:(NSError *_Nullable *)error;
+ (_Nullable instancetype)fromData:(NSData *)data error:(NSError *_Nullable *)error;
- (NSString *_Nullable)toJSON:(NSStringEncoding)encoding error:(NSError *_Nullable *)error;
- (NSData *_Nullable)toData:(NSError *_Nullable *)error;
@end
@interface QTCustomer : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *email;
@property (nonatomic, nullable, copy) id phone;
@end
@interface QTItem : NSObject
@property (nonatomic, copy) NSString *sku;
@property (nonatomic, assign) NSInteger qty;
@property (nonatomic, assign) double price;
@end
NS_ASSUME_NONNULL_END
// QTRoot.m
#import "QTRoot.h"
#define λ(decl, expr) (^(decl) { return (expr); })
static id NSNullify(id _Nullable x) {
return (x == nil || x == NSNull.null) ? NSNull.null : x;
}
NS_ASSUME_NONNULL_BEGIN
@interface QTRoot (JSONConversion)
+ (instancetype)fromJSONDictionary:(NSDictionary *)dict;
- (NSDictionary *)JSONDictionary;
@end
@interface QTCustomer (JSONConversion)
+ (instancetype)fromJSONDictionary:(NSDictionary *)dict;
- (NSDictionary *)JSONDictionary;
@end
@interface QTItem (JSONConversion)
+ (instancetype)fromJSONDictionary:(NSDictionary *)dict;
- (NSDictionary *)JSONDictionary;
@end
static id map(id collection, id (^f)(id value)) {
id result = nil;
if ([collection isKindOfClass:NSArray.class]) {
result = [NSMutableArray arrayWithCapacity:[collection count]];
for (id x in collection) [result addObject:f(x)];
} else if ([collection isKindOfClass:NSDictionary.class]) {
result = [NSMutableDictionary dictionaryWithCapacity:[collection count]];
for (id key in collection) [result setObject:f([collection objectForKey:key]) forKey:key];
}
return result;
}
#pragma mark - JSON serialization
QTRoot *_Nullable QTRootFromData(NSData *data, NSError **error)
{
@try {
id json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:error];
return *error ? nil : [QTRoot fromJSONDictionary:json];
} @catch (NSException *exception) {
*error = [NSError errorWithDomain:@"JSONSerialization" code:-1 userInfo:@{ @"exception": exception }];
return nil;
}
}
QTRoot *_Nullable QTRootFromJSON(NSString *json, NSStringEncoding encoding, NSError **error)
{
return QTRootFromData([json dataUsingEncoding:encoding], error);
}
NSData *_Nullable QTRootToData(QTRoot *root, NSError **error)
{
@try {
id json = [root JSONDictionary];
NSData *data = [NSJSONSerialization dataWithJSONObject:json options:kNilOptions error:error];
return *error ? nil : data;
} @catch (NSException *exception) {
*error = [NSError errorWithDomain:@"JSONSerialization" code:-1 userInfo:@{ @"exception": exception }];
return nil;
}
}
NSString *_Nullable QTRootToJSON(QTRoot *root, NSStringEncoding encoding, NSError **error)
{
NSData *data = QTRootToData(root, error);
return data ? [[NSString alloc] initWithData:data encoding:encoding] : nil;
}
@implementation QTRoot
+ (NSDictionary<NSString *, NSString *> *)properties
{
static NSDictionary<NSString *, NSString *> *properties;
return properties = properties ? properties : @{
@"id": @"identifier",
@"status": @"status",
@"paid": @"isPaid",
@"customer": @"customer",
@"items": @"items",
@"total": @"total",
};
}
+ (_Nullable instancetype)fromData:(NSData *)data error:(NSError *_Nullable *)error
{
return QTRootFromData(data, error);
}
+ (_Nullable instancetype)fromJSON:(NSString *)json encoding:(NSStringEncoding)encoding error:(NSError *_Nullable *)error
{
return QTRootFromJSON(json, encoding, error);
}
+ (instancetype)fromJSONDictionary:(NSDictionary *)dict
{
return dict ? [[QTRoot alloc] initWithJSONDictionary:dict] : nil;
}
- (instancetype)initWithJSONDictionary:(NSDictionary *)dict
{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
_customer = [QTCustomer fromJSONDictionary:(id)_customer];
_items = map(_items, λ(id x, [QTItem fromJSONDictionary:x]));
}
return self;
}
- (void)setValue:(nullable id)value forKey:(NSString *)key
{
id resolved = QTRoot.properties[key];
if (resolved) [super setValue:value forKey:resolved];
}
- (void)setNilValueForKey:(NSString *)key
{
id resolved = QTRoot.properties[key];
if (resolved) [super setValue:@(0) forKey:resolved];
}
- (NSDictionary *)JSONDictionary
{
id dict = [[self dictionaryWithValuesForKeys:QTRoot.properties.allValues] mutableCopy];
for (id jsonName in QTRoot.properties) {
id propertyName = QTRoot.properties[jsonName];
if (![jsonName isEqualToString:propertyName]) {
dict[jsonName] = dict[propertyName];
[dict removeObjectForKey:propertyName];
}
}
[dict addEntriesFromDictionary:@{
@"paid": _isPaid ? @YES : @NO,
@"customer": [_customer JSONDictionary],
@"items": map(_items, λ(id x, [x JSONDictionary])),
}];
return dict;
}
- (NSData *_Nullable)toData:(NSError *_Nullable *)error
{
return QTRootToData(self, error);
}
- (NSString *_Nullable)toJSON:(NSStringEncoding)encoding error:(NSError *_Nullable *)error
{
return QTRootToJSON(self, encoding, error);
}
@end
@implementation QTCustomer
+ (NSDictionary<NSString *, NSString *> *)properties
{
static NSDictionary<NSString *, NSString *> *properties;
return properties = properties ? properties : @{
@"name": @"name",
@"email": @"email",
@"phone": @"phone",
};
}
+ (instancetype)fromJSONDictionary:(NSDictionary *)dict
{
return dict ? [[QTCustomer alloc] initWithJSONDictionary:dict] : nil;
}
- (instancetype)initWithJSONDictionary:(NSDictionary *)dict
{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
- (void)setValue:(nullable id)value forKey:(NSString *)key
{
id resolved = QTCustomer.properties[key];
if (resolved) [super setValue:value forKey:resolved];
}
- (void)setNilValueForKey:(NSString *)key
{
id resolved = QTCustomer.properties[key];
if (resolved) [super setValue:@(0) forKey:resolved];
}
- (NSDictionary *)JSONDictionary
{
return [self dictionaryWithValuesForKeys:QTCustomer.properties.allValues];
}
@end
@implementation QTItem
+ (NSDictionary<NSString *, NSString *> *)properties
{
static NSDictionary<NSString *, NSString *> *properties;
return properties = properties ? properties : @{
@"sku": @"sku",
@"qty": @"qty",
@"price": @"price",
};
}
+ (instancetype)fromJSONDictionary:(NSDictionary *)dict
{
return dict ? [[QTItem alloc] initWithJSONDictionary:dict] : nil;
}
- (instancetype)initWithJSONDictionary:(NSDictionary *)dict
{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
- (void)setValue:(nullable id)value forKey:(NSString *)key
{
id resolved = QTItem.properties[key];
if (resolved) [super setValue:value forKey:resolved];
}
- (void)setNilValueForKey:(NSString *)key
{
id resolved = QTItem.properties[key];
if (resolved) [super setValue:@(0) forKey:resolved];
}
- (NSDictionary *)JSONDictionary
{
return [self dictionaryWithValuesForKeys:QTItem.properties.allValues];
}
@end
NS_ASSUME_NONNULL_END
Look for the "phone" field: sampled as null, it is typed as id (nullable). "items" became NSArray<QTItem *> *, and "total" is double.
Learn more
From JSON sample to Objective-C 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.
Every class is prefixed QT, so a project that already has its own QTCustomer collides on the name. Regenerate with quicktype's class-prefix option to pick a different prefix rather than renaming by hand afterwards, because the +properties dictionaries and the NSNullify helpers reference the class names throughout the generated file.
How JSON values become Objective-C types
Inference works from the values in your sample. Strings map to NSString *, 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 NSArray<QTItem *> * 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 id (nullable). 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 Objective-C 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 NSArray<QTItem *> * 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 NSString * in Objective-C. 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 Objective-C
Objective-C has no serialization attributes, so quicktype records the mapping in code: each class exposes a +properties dictionary pairing wire keys with property names — @"id" with @"identifier", @"paid" with @"isPaid" — which is where renames live. Classes are QT-prefixed and wrapped in NS_ASSUME_NONNULL_BEGIN and END, and every property declares its own memory semantics: copy for NSString and NSArray, strong for nested objects, assign for scalars. Serialization is NSJSONSerialization behind +fromJSON:encoding:error:, +fromData:error:, -toJSON:error: and -toData:, with plain C functions underneath.
A field seen only as null becomes a nullable, untyped id property, so the compiler cannot help you at all. Add a sample where the value is present and it sharpens to NSString * marked _Nullable; a numeric field that is sometimes missing arrives as a nullable NSNumber * rather than a scalar, because NSInteger cannot express absence. The generated NSNullify helper converts nil back to NSNull.null on the way out, so a null field round-trips into the JSON dictionary instead of quietly disappearing from the serialized payload.
Each nested object becomes its own QT-prefixed class — QTCustomer, QTItem — declared with @class forward references at the top of the header and populated through a private +fromJSONDictionary: category. Arrays of objects are typed NSArray<QTItem *> *, a lightweight generic Xcode checks at compile time and bridges into Swift as a typed array. Only the top-level class gets the fromJSON and toJSON entry points. An empty array in the sample generates a bare NSArray * with no element class, and a one-element array hides every optional field.
Help
Frequently asked questions
Everything you need to know about the JSON to Objective-C.
01How do I generate Objective-C 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 Objective-C types, arrays become NSArray<QTItem *> *, and the result downloads as Root.m.
02What does the generated Objective-C code include?
Objective-C generation produces NSObject subclasses with properties, plus JSON (de)serialization via NSJSONSerialization — header and implementation in one output, ready to split into .h/.m files.
03How do I use the generated Objective-C code in my project?
Split the output at the header/implementation boundary into QTRoot.h and QTRoot.m (or keep one file and #import it). Parse with [QTRoot fromJSON:json encoding:NSUTF8StringEncoding error:&error]; the classes are ARC-ready NSObject subclasses backed by NSJSONSerialization, with nullability annotations so they bridge cleanly into Swift.
04Are the generated classes ARC-compatible?
The generated properties use modern attributes and the code assumes ARC, matching any Xcode project from the last decade. Nullability annotations make the models Swift-interop friendly too.
Keep working