Permalink
Browse files

Merge pull request #122 from jjakub/master

Added an optional NSComparator to be used when sorting the output JSON.
  • Loading branch information...
2 parents b4edb29 + e516809 commit 5753adbd7eaa4e6d193cecd41090b2df750a316d @stig committed Feb 16, 2012
View
7 Classes/SBJsonStreamWriter.h
@@ -122,6 +122,13 @@
*/
@property BOOL sortKeys;
+/**
+ @brief An optional comparator to be used if sortKeys is YES.
+
+ If this is nil, sorting will be done via @selector(compare:).
+ */
+@property (copy) NSComparator sortKeysComparator;
+
/// Contains the error description after an error has occured.
@property (copy) NSString *error;
View
12 Classes/SBJsonStreamWriter.m
@@ -48,6 +48,7 @@ @implementation SBJsonStreamWriter
@synthesize stateStack;
@synthesize humanReadable;
@synthesize sortKeys;
+@synthesize sortKeysComparator;
+ (void)initialize {
kNotANumber = [NSDecimalNumber notANumber];
@@ -87,8 +88,15 @@ - (BOOL)writeObject:(NSDictionary *)dict {
return NO;
NSArray *keys = [dict allKeys];
- if (sortKeys)
- keys = [keys sortedArrayUsingSelector:@selector(compare:)];
+
+ if (sortKeys) {
+ if (sortKeysComparator) {
+ keys = [keys sortedArrayWithOptions:NSSortStable usingComparator:sortKeysComparator];
+ }
+ else{
+ keys = [keys sortedArrayUsingSelector:@selector(compare:)];
+ }
+ }
for (id k in keys) {
if (![k isKindOfClass:[NSString class]]) {
View
7 Classes/SBJsonWriter.h
@@ -76,6 +76,13 @@
@property BOOL sortKeys;
/**
+ @brief An optional comparator to be used if sortKeys is YES.
+
+ If this is nil, sorting will be done via @selector(compare:).
+ */
+@property (copy) NSComparator sortKeysComparator;
+
+/**
@brief Return JSON representation for the given object.
Returns a string containing JSON representation of the passed in value, or nil on error.
View
3 Classes/SBJsonWriter.m
@@ -44,6 +44,8 @@ @implementation SBJsonWriter
@synthesize error;
@synthesize maxDepth;
+@synthesize sortKeysComparator;
+
- (id)init {
self = [super init];
if (self) {
@@ -81,6 +83,7 @@ - (NSData*)dataWithObject:(id)object {
SBJsonStreamWriter *streamWriter = [[SBJsonStreamWriter alloc] init];
streamWriter.sortKeys = self.sortKeys;
streamWriter.maxDepth = self.maxDepth;
+ streamWriter.sortKeysComparator = self.sortKeysComparator;
streamWriter.humanReadable = self.humanReadable;
streamWriter.delegate = accumulator;
View
6 SBJson.xcodeproj/project.pbxproj
@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
+ 3BB5955C14EAA4B8001BE91E /* SortedFormatTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB5955B14EAA4B8001BE91E /* SortedFormatTest.m */; };
+ 3BB5955D14EAA4B8001BE91E /* SortedFormatTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB5955B14EAA4B8001BE91E /* SortedFormatTest.m */; };
BC12324B1391D5CC00131607 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BC1232491391D5CC00131607 /* InfoPlist.strings */; };
BC1232561391D5CC00131607 /* SBJson.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC12323D1391D5CC00131607 /* SBJson.framework */; };
BC12325C1391D5CC00131607 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BC12325A1391D5CC00131607 /* InfoPlist.strings */; };
@@ -102,6 +104,7 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ 3BB5955B14EAA4B8001BE91E /* SortedFormatTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SortedFormatTest.m; sourceTree = "<group>"; };
BC12323D1391D5CC00131607 /* SBJson.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SBJson.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BC1232481391D5CC00131607 /* SBJson-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SBJson-Info.plist"; sourceTree = "<group>"; };
BC12324A1391D5CC00131607 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -284,6 +287,7 @@
BC8852311391D6DD00370E55 /* RoundTripTest.m */,
BC8852971391D6DE00370E55 /* StreamParserIntegrationTest.m */,
BC8852981391D6DE00370E55 /* WriterTest.m */,
+ 3BB5955B14EAA4B8001BE91E /* SortedFormatTest.m */,
);
path = Tests;
sourceTree = "<group>";
@@ -610,6 +614,7 @@
BC88532C1391D6DE00370E55 /* RoundTripTest.m in Sources */,
BC8853911391D6DE00370E55 /* StreamParserIntegrationTest.m in Sources */,
BC8853921391D6DE00370E55 /* WriterTest.m in Sources */,
+ 3BB5955C14EAA4B8001BE91E /* SortedFormatTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -644,6 +649,7 @@
BC417FFA13A1008F00C8BC49 /* RoundTripTest.m in Sources */,
BC417FFB13A1008F00C8BC49 /* StreamParserIntegrationTest.m in Sources */,
BC417FFC13A1008F00C8BC49 /* WriterTest.m in Sources */,
+ 3BB5955D14EAA4B8001BE91E /* SortedFormatTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
1 Tests/Data/comparatorsort/basic/input
@@ -0,0 +1 @@
+["one",2,{"foo":null,"Boo":false,"quux":true,"bar":[1, 2, []]},{}]
View
17 Tests/Data/comparatorsort/basic/output
@@ -0,0 +1,17 @@
+[
+ "one",
+ 2,
+ {
+ "bar": [
+ 1,
+ 2,
+ [
+ ]
+ ],
+ "Boo": false,
+ "foo": null,
+ "quux": true
+ },
+ {
+ }
+]
View
22 Tests/Data/comparatorsort/json.org/a/input
@@ -0,0 +1,22 @@
+{
+ "glossary": {
+ "title": "example glossary",
+ "GlossDiv": {
+ "title": "S",
+ "GlossList": {
+ "GlossEntry": {
+ "ID": "SGML",
+ "SortAs": "SGML",
+ "GlossTerm": "Standard Generalized Markup Language",
+ "Acronym": "SGML",
+ "Abbrev": "ISO 8879:1986",
+ "GlossDef": {
+ "para": "A meta-markup language, used to create markup languages such as DocBook.",
+ "GlossSeeAlso": ["GML", "XML"]
+ },
+ "GlossSee": "markup"
+ }
+ }
+ }
+ }
+}
View
25 Tests/Data/comparatorsort/json.org/a/output
@@ -0,0 +1,25 @@
+{
+ "glossary": {
+ "GlossDiv": {
+ "GlossList": {
+ "GlossEntry": {
+ "Abbrev": "ISO 8879:1986",
+ "Acronym": "SGML",
+ "GlossDef": {
+ "GlossSeeAlso": [
+ "GML",
+ "XML"
+ ],
+ "para": "A meta-markup language, used to create markup languages such as DocBook."
+ },
+ "GlossSee": "markup",
+ "GlossTerm": "Standard Generalized Markup Language",
+ "ID": "SGML",
+ "SortAs": "SGML"
+ }
+ },
+ "title": "S"
+ },
+ "title": "example glossary"
+ }
+}
View
13 Tests/Data/comparatorsort/rfc4627/a/input
@@ -0,0 +1,13 @@
+ {
+ "Image": {
+ "Width": 800,
+ "Height": 600,
+ "Title": "View from 15th Floor",
+ "Thumbnail": {
+ "Url": "http://www.example.com/image/481989943",
+ "Height": 125,
+ "Width": "100"
+ },
+ "IDs": [116, 943, 234, 38793]
+ }
+ }
View
18 Tests/Data/comparatorsort/rfc4627/a/output
@@ -0,0 +1,18 @@
+{
+ "Image": {
+ "Height": 600,
+ "IDs": [
+ 116,
+ 943,
+ 234,
+ 38793
+ ],
+ "Thumbnail": {
+ "Height": 125,
+ "Url": "http://www.example.com/image/481989943",
+ "Width": "100"
+ },
+ "Title": "View from 15th Floor",
+ "Width": 800
+ }
+}
View
93 Tests/SortedFormatTest.m
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 2011 Stig Brautaset. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the author nor the names of its contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+#import "JsonTestCase.h"
+
+@interface SortedFormatTest : JsonTestCase
+@end
+
+@implementation SortedFormatTest
+
+- (void)setUp {
+ [super setUp];
+ writer.humanReadable = YES;
+ writer.sortKeys = YES;
+ writer.sortKeysComparator = ^(id obj1, id obj2) {
+ return [obj1 compare:obj2 options:NSCaseInsensitiveSearch|NSLiteralSearch];
+ };
+}
+
+- (void)testString {
+ [self foreachTestInSuite:@"Tests/Data/comparatorsort" apply:^(NSString *inpath, NSString *outpath) {
+ NSError *error = nil;
+ NSString *input = [NSString stringWithContentsOfFile:inpath encoding:NSUTF8StringEncoding error:&error];
+ STAssertNotNil(input, @"%@ - %@", inpath, error);
+
+ NSString *output = [NSString stringWithContentsOfFile:outpath encoding:NSUTF8StringEncoding error:&error];
+ STAssertNotNil(output, @"%@ - %@", outpath, error);
+
+ id object = [parser objectWithString:input];
+ STAssertNotNil(object, nil);
+
+ NSString *json = [writer stringWithObject:object];
+ STAssertNotNil(json, nil);
+
+ json = [json stringByAppendingString:@"\n"];
+ STAssertEqualObjects(json, output, nil);
+ }];
+
+ STAssertEquals(count, (NSUInteger)3, nil);
+}
+
+- (void)testData {
+ [self foreachTestInSuite:@"Tests/Data/comparatorsort" apply:^(NSString *inpath, NSString *outpath) {
+ NSError *error = nil;
+ NSData *input = [NSData dataWithContentsOfFile:inpath];
+ STAssertNotNil(input, @"%@ - %@", inpath, error);
+
+ id object = [parser objectWithData:input];
+ STAssertNotNil(object, nil);
+
+ NSData *json = [writer dataWithObject:object];
+ STAssertNotNil(json, nil);
+
+ NSData *output = [NSData dataWithContentsOfFile:outpath];
+ STAssertNotNil(output, @"%@ - %@", outpath, error);
+
+ output = [NSData dataWithBytes:output.bytes length:output.length-1];
+ STAssertEqualObjects(json, output, nil);
+ }];
+
+ STAssertEquals(count, (NSUInteger)3, nil);
+}
+
+@end

0 comments on commit 5753adb

Please sign in to comment.