Skip to content

Commit ff96b76

Browse files
committed
NSJSONSerialization: Implement NSJSONWritingSortedKeys
1 parent 283ecfe commit ff96b76

3 files changed

Lines changed: 58 additions & 10 deletions

File tree

Headers/Foundation/NSJSONSerialization.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@ enum
5050
* If this is not set, then the writer will not generate any superfluous
5151
* whitespace, producing space-efficient but not very human-friendly JSON.
5252
*/
53-
NSJSONWritingPrettyPrinted = (1UL << 0)
53+
NSJSONWritingPrettyPrinted = (1UL << 0),
54+
#if OS_API_VERSION(MAC_OS_X_VERSION_10_13, GS_API_LATEST)
55+
/**
56+
* When writing JSON, sort keys in lexicographic order.
57+
*/
58+
NSJSONWritingSortedKeys = (1UL << 1)
59+
#endif
5460
};
5561
/**
5662
* A bitmask containing flags from the NSJSONWriting* set, specifying options

Source/NSJSONSerialization.m

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@
860860
}
861861

862862
static BOOL
863-
writeObject(id obj, NSMutableString *output, NSInteger tabs)
863+
writeObject(id obj, NSMutableString *output, NSInteger tabs, NSJSONWritingOptions opt)
864864
{
865865
if ([obj isKindOfClass: NSArrayClass])
866866
{
@@ -874,7 +874,7 @@
874874
writeComma = YES;
875875
writeNewline(output, tabs);
876876
writeTabs(output, tabs);
877-
writeObject(o, output, tabs + 1);
877+
writeObject(o, output, tabs + 1, opt);
878878
END_FOR_IN(obj)
879879
writeNewline(output, tabs);
880880
writeTabs(output, tabs);
@@ -883,8 +883,15 @@
883883
else if ([obj isKindOfClass: NSDictionaryClass])
884884
{
885885
BOOL writeComma = NO;
886+
NSArray *keys = [obj allKeys];
886887
[output appendString: @"{"];
887-
FOR_IN(id, o, obj)
888+
889+
if ((opt & NSJSONWritingSortedKeys) == NSJSONWritingSortedKeys)
890+
{
891+
keys = [keys sortedArrayUsingSelector: @selector(compare:)];
892+
}
893+
894+
FOR_IN(id, o, keys)
888895
// Keys in dictionaries must be strings
889896
if (![o isKindOfClass: NSStringClass]) { return NO; }
890897
if (writeComma)
@@ -894,10 +901,11 @@
894901
writeComma = YES;
895902
writeNewline(output, tabs);
896903
writeTabs(output, tabs);
897-
writeObject(o, output, tabs + 1);
898-
[output appendString: @": "];
899-
writeObject([obj objectForKey: o], output, tabs + 1);
900-
END_FOR_IN(obj)
904+
writeObject(o, output, tabs + 1, opt);
905+
[output appendString: @":"];
906+
writeObject([obj objectForKey: o], output, tabs + 1, opt);
907+
END_FOR_IN(keys)
908+
901909
writeNewline(output, tabs);
902910
writeTabs(output, tabs);
903911
[output appendString: @"}"];
@@ -1062,7 +1070,7 @@ + (NSData*) dataWithJSONObject: (id)obj
10621070

10631071
tabs = ((opt & NSJSONWritingPrettyPrinted) == NSJSONWritingPrettyPrinted) ?
10641072
0 : NSIntegerMin;
1065-
if (writeObject(obj, str, tabs))
1073+
if (writeObject(obj, str, tabs, opt))
10661074
{
10671075
data = [str dataUsingEncoding: NSUTF8StringEncoding];
10681076
if (NULL != error)
@@ -1089,7 +1097,7 @@ + (NSData*) dataWithJSONObject: (id)obj
10891097

10901098
+ (BOOL) isValidJSONObject: (id)obj
10911099
{
1092-
return writeObject(obj, nil, NSIntegerMin);
1100+
return writeObject(obj, nil, NSIntegerMin, 0);
10931101
}
10941102

10951103
+ (id) JSONObjectWithData: (NSData *)data
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#import <Foundation/Foundation.h>
2+
#import "ObjectTesting.h"
3+
4+
void testLexicographicalOrder() {
5+
NSArray *objects =
6+
[NSArray arrayWithObjects:@"a", @"b", @"c", @"d", @"e", nil];
7+
NSArray *keys = [NSArray
8+
arrayWithObjects:@"c_ab", @"a_ab", @"d_ab", @"f_cb", @"f_ab", nil];
9+
NSDictionary *dict = [NSDictionary dictionaryWithObjects:objects
10+
forKeys:keys];
11+
12+
NSError *error = nil;
13+
NSData *actualData =
14+
[NSJSONSerialization dataWithJSONObject:dict
15+
options:NSJSONWritingSortedKeys
16+
error:&error];
17+
PASS_EQUAL(error, nil, "no error occurred during serialisation");
18+
19+
20+
NSString *actual = [[NSString alloc] initWithData:actualData
21+
encoding:NSUTF8StringEncoding];
22+
NSString *expected = @"{\"a_ab\":\"b\",\"c_ab\":\"a\",\"d_ab\":\"c\",\"f_"
23+
"ab\":\"e\",\"f_cb\":\"d\"}";
24+
PASS_EQUAL(actual, expected, "JSON is correctly sorted");
25+
NSLog(@"%@", actual);
26+
}
27+
28+
int main(void) {
29+
NSAutoreleasePool *arp = [NSAutoreleasePool new];
30+
31+
testLexicographicalOrder();
32+
33+
[arp release];
34+
}

0 commit comments

Comments
 (0)