-
Notifications
You must be signed in to change notification settings - Fork 1
/
NSDictionary+BSJSONAdditions.m
191 lines (168 loc) · 7.08 KB
/
NSDictionary+BSJSONAdditions.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//
// BSJSONAdditions
//
// Created by Blake Seely on 2/1/06.
// Copyright 2006 Blake Seely - http://www.blakeseely.com All rights reserved.
// Permission to use this code:
//
// Feel free to use this code in your software, either as-is or
// in a modified form. Either way, please include a credit in
// your software's "About" box or similar, mentioning at least
// my name (Blake Seely).
//
// Permission to redistribute this code:
//
// You can redistribute this code, as long as you keep these
// comments. You can also redistribute modified versions of the
// code, as long as you add comments to say that you've made
// modifications (keeping these original comments too).
//
// If you do use or redistribute this code, an email would be
// appreciated, just to let me know that people are finding my
// code useful. You can reach me at blakeseely@mac.com
#import "NSDictionary+BSJSONAdditions.h"
#import "NSScanner+BSJSONAdditions.h"
NSString *jsonIndentString = @"\t"; // Modify this string to change how the output formats.
const int jsonDoNotIndent = -1;
@implementation NSDictionary (BSJSONAdditions)
+ (NSDictionary *)dictionaryWithJSONString:(NSString *)jsonString
{
NSScanner *scanner = [[NSScanner alloc] initWithString:jsonString];
NSDictionary *dictionary = nil;
[scanner scanJSONObject:&dictionary];
return dictionary;
}
- (NSString *)jsonStringValue
{
return [self jsonStringValueWithIndentLevel:0];
}
@end
@implementation NSDictionary (PrivateBSJSONAdditions)
- (NSString *)jsonStringValueWithIndentLevel:(int)level
{
NSMutableString *jsonString = [[NSMutableString alloc] init];
[jsonString appendString:jsonObjectStartString];
NSEnumerator *keyEnum = [self keyEnumerator];
NSString *keyString = [keyEnum nextObject];
NSString *valueString;
if (keyString != nil) {
valueString = [self jsonStringForValue:[self objectForKey:keyString] withIndentLevel:level];
if (level != jsonDoNotIndent) { // indent before each key
[jsonString appendString:[self jsonIndentStringForLevel:level]];
}
[jsonString appendFormat:@" %@ %@ %@", [self jsonStringForString:keyString], jsonKeyValueSeparatorString, valueString];
}
while (keyString = [keyEnum nextObject]) {
valueString = [self jsonStringForValue:[self objectForKey:keyString] withIndentLevel:level]; // TODO bail if valueString is nil? How to bail successfully from here?
[jsonString appendString:jsonValueSeparatorString];
if (level != jsonDoNotIndent) { // indent before each key
[jsonString appendFormat:@"%@", [self jsonIndentStringForLevel:level]];
}
[jsonString appendFormat:@" %@ %@ %@", [self jsonStringForString:keyString], jsonKeyValueSeparatorString, valueString];
}
//[jsonString appendString:@"\n"];
[jsonString appendString:jsonObjectEndString];
return jsonString;
}
- (NSString *)jsonStringForValue:(id)value withIndentLevel:(int)level
{
NSString *jsonString;
if ([value respondsToSelector:@selector(characterAtIndex:)]) // String
jsonString = [self jsonStringForString:(NSString *)value];
else if ([value respondsToSelector:@selector(keyEnumerator)]) // Dictionary
jsonString = [(NSDictionary *)value jsonStringValueWithIndentLevel:(level + 1)];
else if ([value respondsToSelector:@selector(objectAtIndex:)]) // Array
jsonString = [self jsonStringForArray:(NSArray *)value withIndentLevel:level];
else if (value == [NSNull null]) // null
jsonString = jsonNullString;
else if ([value respondsToSelector:@selector(objCType)]) { // NSNumber - representing true, false, and any form of numeric
NSNumber *number = (NSNumber *)value;
if (((*[number objCType]) == 'c') && ([number boolValue] == YES)) // true
jsonString = jsonTrueString;
else if (((*[number objCType]) == 'c') && ([number boolValue] == NO)) // false
jsonString = jsonFalseString;
else // attempt to handle as a decimal number - int, fractional, exponential
// TODO: values converted from exponential json to dict and back to json do not format as exponential again
jsonString = [[NSDecimalNumber decimalNumberWithDecimal:[number decimalValue]] stringValue];
} else {
// TODO: error condition - it's not any of the types that I know about.
return nil;
}
return jsonString;
}
- (NSString *)jsonStringForArray:(NSArray *)array withIndentLevel:(int)level
{
NSMutableString *jsonString = [[NSMutableString alloc] init];
[jsonString appendString:jsonArrayStartString];
if ([array count] > 0) {
[jsonString appendString:[self jsonStringForValue:[array objectAtIndex:0] withIndentLevel:level]];
}
int i;
for (i = 1; i < [array count]; i++) {
[jsonString appendFormat:@"%@ %@", jsonValueSeparatorString, [self jsonStringForValue:[array objectAtIndex:i] withIndentLevel:level]];
}
[jsonString appendString:jsonArrayEndString];
return jsonString;
}
- (NSString *)jsonStringForString:(NSString *)string
{
NSMutableString *jsonString = [[NSMutableString alloc] init];
[jsonString appendString:jsonStringDelimiterString];
// Build the result one character at a time, inserting escaped characters as necessary
int i;
unichar nextChar;
for (i = 0; i < [string length]; i++) {
nextChar = [string characterAtIndex:i];
switch (nextChar) {
case '\"':
[jsonString appendString:@"\\\""];
break;
case '\\':
[jsonString appendString:@"\\n"];
break;
/* TODO: email out to json group on this - spec says to handlt his, examples and example code don't handle this.
case '\/':
[jsonString appendString:@"\\/"];
break;
*/
case '\b':
[jsonString appendString:@"\\b"];
break;
case '\f':
[jsonString appendString:@"\\f"];
break;
case '\n':
[jsonString appendString:@"\\n"];
break;
case '\r':
[jsonString appendString:@"\\r"];
break;
case '\t':
[jsonString appendString:@"\\t"];
break;
/* TODO: Find and encode unicode characters here?
case '\u':
[jsonString appendString:@"\\n"];
break;
*/
default:
[jsonString appendString:[NSString stringWithCharacters:&nextChar length:1]];
break;
}
}
[jsonString appendString:jsonStringDelimiterString];
return jsonString;
}
- (NSString *)jsonIndentStringForLevel:(int)level
{
NSMutableString *indentString = [[NSMutableString alloc] init];
if (level != jsonDoNotIndent) {
[indentString appendString:@"\n"];
int i;
for (i = 0; i < level; i++) {
[indentString appendString:jsonIndentString];
}
}
return indentString;
}
@end