How to Use Nsjsonserialization

How to use NSJSONSerialization

Your root json object is not a dictionary but an array:

[{"id": "1", "name":"Aaa"}, {"id": "2", "name":"Bbb"}]

This might give you a clear picture of how to handle it:

NSError *e = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &e];

if (!jsonArray) {
NSLog(@"Error parsing JSON: %@", e);
} else {
for(NSDictionary *item in jsonArray) {
NSLog(@"Item: %@", item);
}
}

NSJSONSerialization from NSString

First you will need to convert your NSString to NSData by doing the following

NSData *data = [stringData dataUsingEncoding:NSUTF8StringEncoding];

then simply use the JSONObjectWithData method to convert it to JSON

id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

JSON parsing using NSJSONSerialization in iOS

First of all in your JSON response dictionary, under the key 'RESPONSE' you have a array not a dictionary and that array contains dictionary object.
So to extract username and email ID so as below

NSMutableDictionary *response = [[[json valueForKey:@"RESPONSE"] objectAtIndex:0]mutableCopy];
NSString *username = [response valueForKey:@"username"];
NSString *emailId = [response valueForKey:@"email"];

NSJSONSerialization to plain old object?

Here is my solution, which is not based on a library - as I couldn't find any - but instead using the Foundation and Objective-C runtime methods - as discussed in the comments above:

#import <objc/runtime.h>

NSArray<NSString*>* classPropertyList(id instance) {
NSMutableArray* propList = [NSMutableArray array];
unsigned int numProps = 0;
objc_property_t* props = class_copyPropertyList(object_getClass(instance), &numProps);
for (int i = 0; i < numProps; i++)
[propList addObject:[NSString stringWithUTF8String:property_getName(props[i])]];
free(props);
return propList;
}

NSString* typeOfProperty(Class clazz, NSString* propertyName) {
objc_property_t prop = class_getProperty(clazz, [propertyName UTF8String]);
NSArray<NSString*>* propAttrs = [[NSString stringWithUTF8String:property_getAttributes(prop)] componentsSeparatedByString:@","];
if ([(propAttrs[0]) hasPrefix:@"T@\""])
return [propAttrs[0] componentsSeparatedByString:@"\""][1];
return nil;
}

@implementation JSONMarshallable

- (NSData*)toJSON {
return [self toJSON:self withNullValues:YES];
}

- (NSString*)toJSONString {
return [self toJSONString:self withNullValues:YES];
}

- (NSData*)toJSON:_ withNullValues:(bool)nullables {
NSError* error;
NSDictionary* dic = [self toDictionary:self withNullValues:nullables];
NSData* json = [NSJSONSerialization dataWithJSONObject:dic options:0 error:&error];
if (!json) {
NSLog(@"Error encoding DeviceConfigurationRequest: %@", error);
return nil;
}
return json;
}

- (NSString*) toJSONString:_ withNullValues:(bool)nullables {
NSData* json = [self toJSON:self withNullValues:nullables];
return [[NSString alloc] initWithBytes:[json bytes] length:[json length] encoding:NSUTF8StringEncoding];
}

- (NSDictionary*)toDictionary:_ withNullValues:(bool)nullables {
NSMutableDictionary* dic = [NSMutableDictionary new];
for (id propName in classPropertyList(self)) {
id val = [self valueForKey:propName];
if (!nullables && (val == nil || val == NSNull.null))
continue;
if ([val respondsToSelector:@selector(toDictionary:withNullValues:)])
val = [val toDictionary:val withNullValues:nullables];
[dic setObject:(val == nil ? NSNull.null : val) forKey:propName];
}
return dic;
}

- (instancetype)initWithJSONString:(NSString*)json {
return [self initWithJSON:[json dataUsingEncoding:NSUTF8StringEncoding]];
}

- (instancetype)initWithJSON:(NSData*)json {
NSError* error;
if (json == nil)
return nil;
NSDictionary* dataValues = [NSJSONSerialization JSONObjectWithData:json options:0 error:&error];
if (!dataValues) {
NSLog(@"Error parsing invalid JSON for %@: %@", NSStringFromClass(object_getClass(self)), error);
return nil;
}
return [self initWithDictionary:dataValues];
}

- (instancetype)initWithDictionary:(NSDictionary*)dataValues {
if (dataValues == nil)
return nil;
if (self = [super init])
for (id key in dataValues) {
id val = [dataValues objectForKey:key];
if (![self respondsToSelector:NSSelectorFromString(key)])
continue;
NSString* typeName = typeOfProperty([self class], key);
if ([val isKindOfClass:[NSNull class]]) { // translate NSNull values to something useful, if we can
if (typeName == nil)
continue; // don't try to set nil to non-pointer fields
val = nil;
} else if ([val isKindOfClass:[NSDictionary class]] && typeName != nil)
val = [[NSClassFromString(typeName) alloc] initWithDictionary:val];
[self setValue:val forKey:key];
}
return self;
}

@end

It is then easy to create custom model objects by inheriting from JSONMarshallable, like so:

model.h:

#import "JSONMarshallable.h"

@interface MyModel : JSONMarshallable

@property NSString* stringValue;
@property NSNumber* numericValue;
@property bool boolValue;

@end

model.m:

@implementation MyModel
@end

SomeThingElse.m:

// ...

NSData* someJson;
MyModel* obj = [[MyModel alloc] initWithJSON:someJson];
NSString* jsonObj = [obj toJSONString:nil withNullValues:NO];

Critics are welcome! (I'm not very good at Objective C and probably made a lot of faux pas )

Issues:

  • I can handle nullable numbers with NSNumber* (though C primitives work fine for non-nullable numbers), but I don't know how to represent nullable booleans - i.e. a field that is optional and not encoded when using withNullValues:NO.
  • Sending fields for which there are no properties (for example, the server I work with sends values in both snake-case and underscrore-case to make it easy to parse) throws exception. (solved by using respondsToSelector: and setValue: instead of setValuesForKeysWithDictionary:).
  • Trying to set nil values to primitive-typed fields causes exceptions. (solved by checking for property type and NSNull).
  • Doesn't work at all for nesting objects - i.e. a custom model object with properties that are also custom model objects. (solved by checking for property types and recursing encoding/decoding).
  • Probably doesn't handle arrays well - I have yet to need those in my software, so I haven't implemented proper support (though I verified that encoding simple string arrays works well).

Swift, NSJSONSerialization and NSError

The problem is that you cast the result of the JSON deserialization before
checking for an error. If the JSON data is invalid (e.g. incomplete) then

NSJSONSerialization.JSONObjectWithData(...)

returns nil and

NSJSONSerialization.JSONObjectWithData(...) as NSDictionary

will crash.

Here is a version that checks for the error conditions correctly:

var error:NSError? = nil
if let jsonObject: AnyObject = NSJSONSerialization.JSONObjectWithData(receivedData, options: nil, error:&error) {
if let dict = jsonObject as? NSDictionary {
println(dict)
} else {
println("not a dictionary")
}
} else {
println("Could not parse JSON: \(error!)")
}

Remarks:

  • The correct way to check for an error is to test the return value, not the
    error variable.
  • The JSON reading option .AllowFragments does not help here. Setting this option
    only allows that top-level objects that are not an instance of NSArray or NSDictionary, for example

    { "someString" }

You can also do it in one line, with an optional cast as?:

if let dict = NSJSONSerialization.JSONObjectWithData(receivedData, options: nil, error:nil) as? NSDictionary {
println(dict)
} else {
println("Could not read JSON dictionary")
}

The disadvantage is that in the else case you cannot distinguish whether reading
the JSON data failed or if the JSON did not represent a dictionary.

For an update to Swift 3, see LightningStryk's answer.

NSJSONSerialization adding \n to beginning and end of JSON string

First, the output is absolutely correct. It is fine to add spaces and newlines in JSON in the right places. And JSON parser will be able to parse it.

Why does it happen? Because you were absolutely careless in your cde. Look at the option that you pass. It is an option that is used when you parse JSON, not when you write JSON. Why are you passing an option for reading JSON to a method that writes JSON? Now go and look at the writing options, and look which writing option has exactly the same value as the reading option that you passed.

NSJSONSerialization.JSONObjectWithData changes field type

It doesn't. The JSON deserialisation respects the data type and will maintain it. You can't tell the data type from a simple description log, you need to actually interrogate the class. The description log will quote some things if it makes more sense for the human reader, like spaces in the description, but it also omits quotes in some cases.

NSJSONSerialization with C# Rest Service

Your JSON string is missing a closing ] somewhere.

Assuming the string you posted is exactly what was returned by the server, you will also probably need to remove the backslashes in order to make it valid JSON.

NSString *responseString = [[[NSString alloc] initWithData:response] stringByReplacingOccurrencesOfString:@"\\" withString:@""];
NSDictionary *fields = [NSJSONSerialization JSONObjectWithData:responseString options:kNilOptions error:&error];

How to convert BOOL to JSON with NSJSONSerialization?

Use NSNumber:

@{ 
@"foo": @YES
}


Related Topics



Leave a reply



Submit