diff --git a/Headers/Foundation/NSJSONSerialization.h b/Headers/Foundation/NSJSONSerialization.h index 7c78175ca..25fa724d2 100644 --- a/Headers/Foundation/NSJSONSerialization.h +++ b/Headers/Foundation/NSJSONSerialization.h @@ -50,7 +50,13 @@ enum * If this is not set, then the writer will not generate any superfluous * whitespace, producing space-efficient but not very human-friendly JSON. */ - NSJSONWritingPrettyPrinted = (1UL << 0) + NSJSONWritingPrettyPrinted = (1UL << 0), +#if OS_API_VERSION(MAC_OS_X_VERSION_10_13, GS_API_LATEST) + /** + * When writing JSON, sort keys in lexicographic order. + */ + NSJSONWritingSortedKeys = (1UL << 1) +#endif }; /** * A bitmask containing flags from the NSJSONWriting* set, specifying options diff --git a/Source/NSJSONSerialization.m b/Source/NSJSONSerialization.m index 15072670c..a388e3694 100644 --- a/Source/NSJSONSerialization.m +++ b/Source/NSJSONSerialization.m @@ -860,7 +860,7 @@ writeNewline(NSMutableString *output, NSInteger tabs) } static BOOL -writeObject(id obj, NSMutableString *output, NSInteger tabs) +writeObject(id obj, NSMutableString *output, NSInteger tabs, NSJSONWritingOptions opt) { if ([obj isKindOfClass: NSArrayClass]) { @@ -874,7 +874,7 @@ writeObject(id obj, NSMutableString *output, NSInteger tabs) writeComma = YES; writeNewline(output, tabs); writeTabs(output, tabs); - writeObject(o, output, tabs + 1); + writeObject(o, output, tabs + 1, opt); END_FOR_IN(obj) writeNewline(output, tabs); writeTabs(output, tabs); @@ -883,8 +883,15 @@ writeObject(id obj, NSMutableString *output, NSInteger tabs) else if ([obj isKindOfClass: NSDictionaryClass]) { BOOL writeComma = NO; + NSArray *keys = [obj allKeys]; [output appendString: @"{"]; - FOR_IN(id, o, obj) + + if ((opt & NSJSONWritingSortedKeys) == NSJSONWritingSortedKeys) + { + keys = [keys sortedArrayUsingSelector: @selector(compare:)]; + } + + FOR_IN(id, o, keys) // Keys in dictionaries must be strings if (![o isKindOfClass: NSStringClass]) { return NO; } if (writeComma) @@ -894,10 +901,11 @@ writeObject(id obj, NSMutableString *output, NSInteger tabs) writeComma = YES; writeNewline(output, tabs); writeTabs(output, tabs); - writeObject(o, output, tabs + 1); - [output appendString: @": "]; - writeObject([obj objectForKey: o], output, tabs + 1); - END_FOR_IN(obj) + writeObject(o, output, tabs + 1, opt); + [output appendString: @":"]; + writeObject([obj objectForKey: o], output, tabs + 1, opt); + END_FOR_IN(keys) + writeNewline(output, tabs); writeTabs(output, tabs); [output appendString: @"}"]; @@ -1062,7 +1070,7 @@ writeObject(id obj, NSMutableString *output, NSInteger tabs) tabs = ((opt & NSJSONWritingPrettyPrinted) == NSJSONWritingPrettyPrinted) ? 0 : NSIntegerMin; - if (writeObject(obj, str, tabs)) + if (writeObject(obj, str, tabs, opt)) { data = [str dataUsingEncoding: NSUTF8StringEncoding]; if (NULL != error) @@ -1089,7 +1097,7 @@ writeObject(id obj, NSMutableString *output, NSInteger tabs) + (BOOL) isValidJSONObject: (id)obj { - return writeObject(obj, nil, NSIntegerMin); + return writeObject(obj, nil, NSIntegerMin, 0); } + (id) JSONObjectWithData: (NSData *)data diff --git a/Tests/base/NSJSONSerialization/sorted.m b/Tests/base/NSJSONSerialization/sorted.m new file mode 100644 index 000000000..ee8294434 --- /dev/null +++ b/Tests/base/NSJSONSerialization/sorted.m @@ -0,0 +1,34 @@ +#import +#import "ObjectTesting.h" + +void testLexicographicalOrder() { + NSArray *objects = + [NSArray arrayWithObjects:@"a", @"b", @"c", @"d", @"e", nil]; + NSArray *keys = [NSArray + arrayWithObjects:@"c_ab", @"a_ab", @"d_ab", @"f_cb", @"f_ab", nil]; + NSDictionary *dict = [NSDictionary dictionaryWithObjects:objects + forKeys:keys]; + + NSError *error = nil; + NSData *actualData = + [NSJSONSerialization dataWithJSONObject:dict + options:NSJSONWritingSortedKeys + error:&error]; + PASS_EQUAL(error, nil, "no error occurred during serialisation"); + + + NSString *actual = [[NSString alloc] initWithData:actualData + encoding:NSUTF8StringEncoding]; + NSString *expected = @"{\"a_ab\":\"b\",\"c_ab\":\"a\",\"d_ab\":\"c\",\"f_" + "ab\":\"e\",\"f_cb\":\"d\"}"; + PASS_EQUAL(actual, expected, "JSON is correctly sorted"); + NSLog(@"%@", actual); +} + +int main(void) { + NSAutoreleasePool *arp = [NSAutoreleasePool new]; + + testLexicographicalOrder(); + + [arp release]; +} \ No newline at end of file