Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool
Objective-C JSON Formatting for Legacy iOS Applications
If you maintain an older iOS app that still ships Objective-C, JSON work usually comes down to one Foundation class: NSJSONSerialization. It is still Apple's built-in way to turn Foundation objects into JSON and parse JSON back into NSDictionary, NSArray, NSString, NSNumber, and NSNull.
For a real legacy codebase, the hard part is rarely "how do I call the API?" It is avoiding invalid payloads, handling null safely, understanding which formatting options exist on the OS versions you still support, and quickly checking whether a broken response is malformed JSON or just unexpected data. That is where a JSON formatter is useful before you even touch application code.
What Still Matters in Legacy Objective-C
NSJSONSerialization is still the right default for formatting and parsing JSON in Objective-C. Apple's current documentation also notes that the class is thread-safe on iOS 7 and later, so modernized legacy apps do not need a third-party formatter just to serialize normal API payloads.
- Use
+isValidJSONObject:before serializing anything that may contain custom model objects, dates, sets, or computed numeric values. - Remember that JSON
nullbecomes[NSNull null], notnil. - JSON numbers map to
NSNumber, including booleans. - By default, Apple expects a top-level array or dictionary when writing JSON. Scalar values are a special case and should be handled deliberately with fragment options.
Serialize Objective-C Objects Into Readable JSON
When you are logging a request body, creating a fixture, or saving local state, a good serializer path does three things: validates the object first, enables readable formatting only when it helps, and keeps newer options behind availability checks.
Safer Serialization Example
NSDictionary *payload = @{
@"name": @"Alice",
@"age": @(30),
@"active": @(YES),
@"roles": @[@"admin", @"editor"],
@"manager": [NSNull null]
};
if (![NSJSONSerialization isValidJSONObject:payload]) {
NSLog(@"Payload contains a non-JSON type.");
return;
}
NSJSONWritingOptions options = 0;
options |= NSJSONWritingPrettyPrinted;
if (@available(iOS 11.0, *)) {
options |= NSJSONWritingSortedKeys;
}
if (@available(iOS 13.0, *)) {
options |= NSJSONWritingWithoutEscapingSlashes;
}
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:payload
options:options
error:&error];
if (jsonData == nil) {
NSLog(@"Serialization failed: %@", error.localizedDescription);
return;
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData
encoding:NSUTF8StringEncoding];
NSLog(@"%@", jsonString);Why This Pattern Holds Up
isValidJSONObject:catches unsupported types before you get a runtime failure.NSJSONWritingPrettyPrintedis best for logs, fixtures, and local debugging. For network traffic, use0unless humans need to read the payload.NSJSONWritingSortedKeysis available on iOS 11 and later and is useful when you want stable, diff-friendly output in tests or support logs.NSJSONWritingWithoutEscapingSlashesis available on iOS 13 and later. It improves readability for URLs, but it is cosmetic rather than semantic.- JSON does not allow
NaNor infinity values. If floating-point calculations can produce them, sanitize those numbers before serializing.
Parse API Responses Without Common Legacy Bugs
Deserialization looks simple until you hit responses with nullable fields, unexpected top-level values, or code that assumes every response is a dictionary. The safe approach is to parse into id, confirm the root type, then unwrap any NSNull values before using them.
Defensive Parsing Example
NSData *responseData = /* data from NSURLSession */;
NSJSONReadingOptions options = 0;
BOOL expectsScalarJSON = NO;
if (expectsScalarJSON) {
options |= NSJSONReadingFragmentsAllowed;
}
NSError *error = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:responseData
options:options
error:&error];
if (jsonObject == nil) {
NSLog(@"Parse failed: %@", error.localizedDescription);
return;
}
if (![jsonObject isKindOfClass:[NSDictionary class]]) {
NSLog(@"Expected a dictionary but got: %@", [jsonObject class]);
return;
}
NSDictionary *response = (NSDictionary *)jsonObject;
id emailValue = response[@"email"];
NSString *email = (emailValue == [NSNull null]) ? nil : emailValue;
NSLog(@"Parsed email: %@", email);Current Parsing Notes Worth Knowing
- Prefer
NSJSONReadingFragmentsAllowedfor top-level scalar JSON. Older code often usesNSJSONReadingAllowFragments, which Apple now marks as deprecated. NSJSONReadingMutableContainersis only useful if you truly need mutable arrays or dictionaries immediately after parsing. In most maintenance work, immutable results plus targeted copies are cleaner.NSJSONReadingMutableLeavesis rarely worth using in practice.NSJSONReadingJSON5Allowedexists on iOS 15 and later, but it is a niche tool for human-edited local content. Do not quietly enable it for normal API traffic if you expect strict JSON from a backend.
Compatibility and Interoperability Caveats
Apple's API behavior and the JSON standard are close, but not identical in defaults. RFC 8259 says a JSON text can be any serialized value, while NSJSONSerialization still treats top-level fragments as opt-in behavior. That difference matters when an API returns a bare string, number, or boolean.
- If your app still supports iOS 10 or earlier, guard
NSJSONWritingSortedKeyswith@available. - If your app still supports iOS 12 or earlier, do the same for
NSJSONWritingWithoutEscapingSlashes. - If you maintain a mixed fleet of old app versions, keep your JSON generator conservative so responses remain easy to compare across builds and devices.
- Output key sorting helps humans and test diffs, but JSON object key order should not be treated as business logic.
Common Breakages in Legacy Apps
- Custom model objects in payloads: Convert them to dictionaries before calling
dataWithJSONObject:options:error:. - Dates and URLs: Serialize them explicitly as ISO 8601 strings or other agreed wire formats instead of passing
NSDateorNSURLdirectly. - Blind dictionary casts: Some APIs return arrays at the root. Check the type before subscripting.
- UI freezes on large payloads: Pretty-printing or parsing big JSON on the main thread is still expensive. Move heavy JSON work off the UI path.
- Assuming malformed data is valid JSON: Before changing parsing code, drop the payload into an offline formatter to verify whether the issue is syntax, structure, or unexpected nulls.
When a JSON Formatter Helps More Than More Code
For legacy iOS maintenance, a formatter is often the fastest first step. Paste the raw response fromNSURLSession, a proxy capture, or an app log and confirm three things before editing Objective-C: whether the payload is valid JSON, what the real root type is, and which fields are actually null.
That workflow is especially useful when you are debugging production-like data, building test fixtures, or comparing server responses without sending potentially sensitive payloads through another web service.
Bottom Line
Objective-C JSON formatting in 2026 is still mostly about using NSJSONSerialization carefully, not replacing it. Validate objects before writing, guard newer formatting options by OS version, treat NSNull as a first-class case, and use an offline formatter to inspect real payloads before you change legacy parsing code.
Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool