root / Classes / SBJsonWriter.m @ 72744ed1
History | View | Annotate | Download (7.8 kB)
1 | 700184fb | Miltiadis Vasilakis | /* |
---|---|---|---|
2 | 700184fb | Miltiadis Vasilakis | Copyright (C) 2009 Stig Brautaset. All rights reserved. |
3 | 700184fb | Miltiadis Vasilakis | |
4 | 700184fb | Miltiadis Vasilakis | Redistribution and use in source and binary forms, with or without |
5 | 700184fb | Miltiadis Vasilakis | modification, are permitted provided that the following conditions are met: |
6 | 700184fb | Miltiadis Vasilakis | |
7 | 700184fb | Miltiadis Vasilakis | * Redistributions of source code must retain the above copyright notice, this |
8 | 700184fb | Miltiadis Vasilakis | list of conditions and the following disclaimer. |
9 | 700184fb | Miltiadis Vasilakis | |
10 | 700184fb | Miltiadis Vasilakis | * Redistributions in binary form must reproduce the above copyright notice, |
11 | 700184fb | Miltiadis Vasilakis | this list of conditions and the following disclaimer in the documentation |
12 | 700184fb | Miltiadis Vasilakis | and/or other materials provided with the distribution. |
13 | 700184fb | Miltiadis Vasilakis | |
14 | 700184fb | Miltiadis Vasilakis | * Neither the name of the author nor the names of its contributors may be used |
15 | 700184fb | Miltiadis Vasilakis | to endorse or promote products derived from this software without specific |
16 | 700184fb | Miltiadis Vasilakis | prior written permission. |
17 | 700184fb | Miltiadis Vasilakis | |
18 | 700184fb | Miltiadis Vasilakis | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
19 | 700184fb | Miltiadis Vasilakis | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 | 700184fb | Miltiadis Vasilakis | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
21 | 700184fb | Miltiadis Vasilakis | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
22 | 700184fb | Miltiadis Vasilakis | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 | 700184fb | Miltiadis Vasilakis | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
24 | 700184fb | Miltiadis Vasilakis | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
25 | 700184fb | Miltiadis Vasilakis | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
26 | 700184fb | Miltiadis Vasilakis | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
27 | 700184fb | Miltiadis Vasilakis | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | 700184fb | Miltiadis Vasilakis | */ |
29 | 700184fb | Miltiadis Vasilakis | |
30 | 700184fb | Miltiadis Vasilakis | #import "SBJsonWriter.h" |
31 | 700184fb | Miltiadis Vasilakis | |
32 | 700184fb | Miltiadis Vasilakis | @interface SBJsonWriter () |
33 | 700184fb | Miltiadis Vasilakis | |
34 | 700184fb | Miltiadis Vasilakis | - (BOOL)appendValue:(id)fragment into:(NSMutableString*)json; |
35 | 700184fb | Miltiadis Vasilakis | - (BOOL)appendArray:(NSArray*)fragment into:(NSMutableString*)json; |
36 | 700184fb | Miltiadis Vasilakis | - (BOOL)appendDictionary:(NSDictionary*)fragment into:(NSMutableString*)json; |
37 | 700184fb | Miltiadis Vasilakis | - (BOOL)appendString:(NSString*)fragment into:(NSMutableString*)json; |
38 | 700184fb | Miltiadis Vasilakis | |
39 | 700184fb | Miltiadis Vasilakis | - (NSString*)indent; |
40 | 700184fb | Miltiadis Vasilakis | |
41 | 700184fb | Miltiadis Vasilakis | @end |
42 | 700184fb | Miltiadis Vasilakis | |
43 | 700184fb | Miltiadis Vasilakis | @implementation SBJsonWriter |
44 | 700184fb | Miltiadis Vasilakis | |
45 | 700184fb | Miltiadis Vasilakis | static NSMutableCharacterSet *kEscapeChars; |
46 | 700184fb | Miltiadis Vasilakis | |
47 | 700184fb | Miltiadis Vasilakis | + (void)initialize { |
48 | 700184fb | Miltiadis Vasilakis | kEscapeChars = [[NSMutableCharacterSet characterSetWithRange: NSMakeRange(0,32)] retain]; |
49 | 700184fb | Miltiadis Vasilakis | [kEscapeChars addCharactersInString: @"\"\\"]; |
50 | 700184fb | Miltiadis Vasilakis | } |
51 | 700184fb | Miltiadis Vasilakis | |
52 | 700184fb | Miltiadis Vasilakis | |
53 | 700184fb | Miltiadis Vasilakis | @synthesize sortKeys; |
54 | 700184fb | Miltiadis Vasilakis | @synthesize humanReadable; |
55 | 700184fb | Miltiadis Vasilakis | |
56 | 700184fb | Miltiadis Vasilakis | /** |
57 | 700184fb | Miltiadis Vasilakis | @deprecated This exists in order to provide fragment support in older APIs in one more version. |
58 | 700184fb | Miltiadis Vasilakis | It should be removed in the next major version. |
59 | 700184fb | Miltiadis Vasilakis | */ |
60 | 700184fb | Miltiadis Vasilakis | - (NSString*)stringWithFragment:(id)value { |
61 | 700184fb | Miltiadis Vasilakis | [self clearErrorTrace]; |
62 | 700184fb | Miltiadis Vasilakis | depth = 0; |
63 | 700184fb | Miltiadis Vasilakis | NSMutableString *json = [NSMutableString stringWithCapacity:128]; |
64 | 700184fb | Miltiadis Vasilakis | |
65 | 700184fb | Miltiadis Vasilakis | if ([self appendValue:value into:json]) |
66 | 700184fb | Miltiadis Vasilakis | return json; |
67 | 700184fb | Miltiadis Vasilakis | |
68 | 700184fb | Miltiadis Vasilakis | return nil; |
69 | 700184fb | Miltiadis Vasilakis | } |
70 | 700184fb | Miltiadis Vasilakis | |
71 | 700184fb | Miltiadis Vasilakis | |
72 | 700184fb | Miltiadis Vasilakis | - (NSString*)stringWithObject:(id)value { |
73 | 700184fb | Miltiadis Vasilakis | |
74 | 700184fb | Miltiadis Vasilakis | if ([value isKindOfClass:[NSDictionary class]] || [value isKindOfClass:[NSArray class]]) { |
75 | 700184fb | Miltiadis Vasilakis | return [self stringWithFragment:value]; |
76 | 700184fb | Miltiadis Vasilakis | } |
77 | 700184fb | Miltiadis Vasilakis | |
78 | 700184fb | Miltiadis Vasilakis | if ([value respondsToSelector:@selector(proxyForJson)]) { |
79 | 700184fb | Miltiadis Vasilakis | NSString *tmp = [self stringWithObject:[value proxyForJson]]; |
80 | 700184fb | Miltiadis Vasilakis | if (tmp) |
81 | 700184fb | Miltiadis Vasilakis | return tmp; |
82 | 700184fb | Miltiadis Vasilakis | } |
83 | 700184fb | Miltiadis Vasilakis | |
84 | 700184fb | Miltiadis Vasilakis | |
85 | 700184fb | Miltiadis Vasilakis | [self clearErrorTrace]; |
86 | 700184fb | Miltiadis Vasilakis | [self addErrorWithCode:EFRAGMENT description:@"Not valid type for JSON"]; |
87 | 700184fb | Miltiadis Vasilakis | return nil; |
88 | 700184fb | Miltiadis Vasilakis | } |
89 | 700184fb | Miltiadis Vasilakis | |
90 | 700184fb | Miltiadis Vasilakis | |
91 | 700184fb | Miltiadis Vasilakis | - (NSString*)indent { |
92 | 700184fb | Miltiadis Vasilakis | return [@"\n" stringByPaddingToLength:1 + 2 * depth withString:@" " startingAtIndex:0]; |
93 | 700184fb | Miltiadis Vasilakis | } |
94 | 700184fb | Miltiadis Vasilakis | |
95 | 700184fb | Miltiadis Vasilakis | - (BOOL)appendValue:(id)fragment into:(NSMutableString*)json { |
96 | 700184fb | Miltiadis Vasilakis | if ([fragment isKindOfClass:[NSDictionary class]]) { |
97 | 700184fb | Miltiadis Vasilakis | if (![self appendDictionary:fragment into:json]) |
98 | 700184fb | Miltiadis Vasilakis | return NO; |
99 | 700184fb | Miltiadis Vasilakis | |
100 | 700184fb | Miltiadis Vasilakis | } else if ([fragment isKindOfClass:[NSArray class]]) { |
101 | 700184fb | Miltiadis Vasilakis | if (![self appendArray:fragment into:json]) |
102 | 700184fb | Miltiadis Vasilakis | return NO; |
103 | 700184fb | Miltiadis Vasilakis | |
104 | 700184fb | Miltiadis Vasilakis | } else if ([fragment isKindOfClass:[NSString class]]) { |
105 | 700184fb | Miltiadis Vasilakis | if (![self appendString:fragment into:json]) |
106 | 700184fb | Miltiadis Vasilakis | return NO; |
107 | 700184fb | Miltiadis Vasilakis | |
108 | 700184fb | Miltiadis Vasilakis | } else if ([fragment isKindOfClass:[NSNumber class]]) { |
109 | 700184fb | Miltiadis Vasilakis | if ('c' == *[fragment objCType]) |
110 | 700184fb | Miltiadis Vasilakis | [json appendString:[fragment boolValue] ? @"true" : @"false"]; |
111 | 700184fb | Miltiadis Vasilakis | else |
112 | 700184fb | Miltiadis Vasilakis | [json appendString:[fragment stringValue]]; |
113 | 700184fb | Miltiadis Vasilakis | |
114 | 700184fb | Miltiadis Vasilakis | } else if ([fragment isKindOfClass:[NSNull class]]) { |
115 | 700184fb | Miltiadis Vasilakis | [json appendString:@"null"]; |
116 | 700184fb | Miltiadis Vasilakis | } else if ([fragment respondsToSelector:@selector(proxyForJson)]) { |
117 | 700184fb | Miltiadis Vasilakis | [self appendValue:[fragment proxyForJson] into:json]; |
118 | 700184fb | Miltiadis Vasilakis | |
119 | 700184fb | Miltiadis Vasilakis | } else { |
120 | 700184fb | Miltiadis Vasilakis | [self addErrorWithCode:EUNSUPPORTED description:[NSString stringWithFormat:@"JSON serialisation not supported for %@", [fragment class]]]; |
121 | 700184fb | Miltiadis Vasilakis | return NO; |
122 | 700184fb | Miltiadis Vasilakis | } |
123 | 700184fb | Miltiadis Vasilakis | return YES; |
124 | 700184fb | Miltiadis Vasilakis | } |
125 | 700184fb | Miltiadis Vasilakis | |
126 | 700184fb | Miltiadis Vasilakis | - (BOOL)appendArray:(NSArray*)fragment into:(NSMutableString*)json { |
127 | 700184fb | Miltiadis Vasilakis | if (maxDepth && ++depth > maxDepth) { |
128 | 700184fb | Miltiadis Vasilakis | [self addErrorWithCode:EDEPTH description: @"Nested too deep"]; |
129 | 700184fb | Miltiadis Vasilakis | return NO; |
130 | 700184fb | Miltiadis Vasilakis | } |
131 | 700184fb | Miltiadis Vasilakis | [json appendString:@"["]; |
132 | 700184fb | Miltiadis Vasilakis | |
133 | 700184fb | Miltiadis Vasilakis | BOOL addComma = NO; |
134 | 700184fb | Miltiadis Vasilakis | for (id value in fragment) { |
135 | 700184fb | Miltiadis Vasilakis | if (addComma) |
136 | 700184fb | Miltiadis Vasilakis | [json appendString:@","]; |
137 | 700184fb | Miltiadis Vasilakis | else |
138 | 700184fb | Miltiadis Vasilakis | addComma = YES; |
139 | 700184fb | Miltiadis Vasilakis | |
140 | 700184fb | Miltiadis Vasilakis | if ([self humanReadable]) |
141 | 700184fb | Miltiadis Vasilakis | [json appendString:[self indent]]; |
142 | 700184fb | Miltiadis Vasilakis | |
143 | 700184fb | Miltiadis Vasilakis | if (![self appendValue:value into:json]) { |
144 | 700184fb | Miltiadis Vasilakis | return NO; |
145 | 700184fb | Miltiadis Vasilakis | } |
146 | 700184fb | Miltiadis Vasilakis | } |
147 | 700184fb | Miltiadis Vasilakis | |
148 | 700184fb | Miltiadis Vasilakis | depth--; |
149 | 700184fb | Miltiadis Vasilakis | if ([self humanReadable] && [fragment count]) |
150 | 700184fb | Miltiadis Vasilakis | [json appendString:[self indent]]; |
151 | 700184fb | Miltiadis Vasilakis | [json appendString:@"]"]; |
152 | 700184fb | Miltiadis Vasilakis | return YES; |
153 | 700184fb | Miltiadis Vasilakis | } |
154 | 700184fb | Miltiadis Vasilakis | |
155 | 700184fb | Miltiadis Vasilakis | - (BOOL)appendDictionary:(NSDictionary*)fragment into:(NSMutableString*)json { |
156 | 700184fb | Miltiadis Vasilakis | if (maxDepth && ++depth > maxDepth) { |
157 | 700184fb | Miltiadis Vasilakis | [self addErrorWithCode:EDEPTH description: @"Nested too deep"]; |
158 | 700184fb | Miltiadis Vasilakis | return NO; |
159 | 700184fb | Miltiadis Vasilakis | } |
160 | 700184fb | Miltiadis Vasilakis | [json appendString:@"{"]; |
161 | 700184fb | Miltiadis Vasilakis | |
162 | 700184fb | Miltiadis Vasilakis | NSString *colon = [self humanReadable] ? @" : " : @":"; |
163 | 700184fb | Miltiadis Vasilakis | BOOL addComma = NO; |
164 | 700184fb | Miltiadis Vasilakis | NSArray *keys = [fragment allKeys]; |
165 | 700184fb | Miltiadis Vasilakis | if (self.sortKeys) |
166 | 700184fb | Miltiadis Vasilakis | keys = [keys sortedArrayUsingSelector:@selector(compare:)]; |
167 | 700184fb | Miltiadis Vasilakis | |
168 | 700184fb | Miltiadis Vasilakis | for (id value in keys) { |
169 | 700184fb | Miltiadis Vasilakis | if (addComma) |
170 | 700184fb | Miltiadis Vasilakis | [json appendString:@","]; |
171 | 700184fb | Miltiadis Vasilakis | else |
172 | 700184fb | Miltiadis Vasilakis | addComma = YES; |
173 | 700184fb | Miltiadis Vasilakis | |
174 | 700184fb | Miltiadis Vasilakis | if ([self humanReadable]) |
175 | 700184fb | Miltiadis Vasilakis | [json appendString:[self indent]]; |
176 | 700184fb | Miltiadis Vasilakis | |
177 | 700184fb | Miltiadis Vasilakis | if (![value isKindOfClass:[NSString class]]) { |
178 | 700184fb | Miltiadis Vasilakis | [self addErrorWithCode:EUNSUPPORTED description: @"JSON object key must be string"]; |
179 | 700184fb | Miltiadis Vasilakis | return NO; |
180 | 700184fb | Miltiadis Vasilakis | } |
181 | 700184fb | Miltiadis Vasilakis | |
182 | 700184fb | Miltiadis Vasilakis | if (![self appendString:value into:json]) |
183 | 700184fb | Miltiadis Vasilakis | return NO; |
184 | 700184fb | Miltiadis Vasilakis | |
185 | 700184fb | Miltiadis Vasilakis | [json appendString:colon]; |
186 | 700184fb | Miltiadis Vasilakis | if (![self appendValue:[fragment objectForKey:value] into:json]) { |
187 | 700184fb | Miltiadis Vasilakis | [self addErrorWithCode:EUNSUPPORTED description:[NSString stringWithFormat:@"Unsupported value for key %@ in object", value]]; |
188 | 700184fb | Miltiadis Vasilakis | return NO; |
189 | 700184fb | Miltiadis Vasilakis | } |
190 | 700184fb | Miltiadis Vasilakis | } |
191 | 700184fb | Miltiadis Vasilakis | |
192 | 700184fb | Miltiadis Vasilakis | depth--; |
193 | 700184fb | Miltiadis Vasilakis | if ([self humanReadable] && [fragment count]) |
194 | 700184fb | Miltiadis Vasilakis | [json appendString:[self indent]]; |
195 | 700184fb | Miltiadis Vasilakis | [json appendString:@"}"]; |
196 | 700184fb | Miltiadis Vasilakis | return YES; |
197 | 700184fb | Miltiadis Vasilakis | } |
198 | 700184fb | Miltiadis Vasilakis | |
199 | 700184fb | Miltiadis Vasilakis | - (BOOL)appendString:(NSString*)fragment into:(NSMutableString*)json { |
200 | 700184fb | Miltiadis Vasilakis | |
201 | 700184fb | Miltiadis Vasilakis | [json appendString:@"\""]; |
202 | 700184fb | Miltiadis Vasilakis | |
203 | 700184fb | Miltiadis Vasilakis | NSRange esc = [fragment rangeOfCharacterFromSet:kEscapeChars]; |
204 | 700184fb | Miltiadis Vasilakis | if ( !esc.length ) { |
205 | 700184fb | Miltiadis Vasilakis | // No special chars -- can just add the raw string: |
206 | 700184fb | Miltiadis Vasilakis | [json appendString:fragment]; |
207 | 700184fb | Miltiadis Vasilakis | |
208 | 700184fb | Miltiadis Vasilakis | } else { |
209 | 700184fb | Miltiadis Vasilakis | NSUInteger length = [fragment length]; |
210 | 700184fb | Miltiadis Vasilakis | for (NSUInteger i = 0; i < length; i++) { |
211 | 700184fb | Miltiadis Vasilakis | unichar uc = [fragment characterAtIndex:i]; |
212 | 700184fb | Miltiadis Vasilakis | switch (uc) { |
213 | 700184fb | Miltiadis Vasilakis | case '"': [json appendString:@"\\\""]; break; |
214 | 700184fb | Miltiadis Vasilakis | case '\\': [json appendString:@"\\\\"]; break; |
215 | 700184fb | Miltiadis Vasilakis | case '\t': [json appendString:@"\\t"]; break; |
216 | 700184fb | Miltiadis Vasilakis | case '\n': [json appendString:@"\\n"]; break; |
217 | 700184fb | Miltiadis Vasilakis | case '\r': [json appendString:@"\\r"]; break; |
218 | 700184fb | Miltiadis Vasilakis | case '\b': [json appendString:@"\\b"]; break; |
219 | 700184fb | Miltiadis Vasilakis | case '\f': [json appendString:@"\\f"]; break; |
220 | 700184fb | Miltiadis Vasilakis | default: |
221 | 700184fb | Miltiadis Vasilakis | if (uc < 0x20) { |
222 | 700184fb | Miltiadis Vasilakis | [json appendFormat:@"\\u%04x", uc]; |
223 | 700184fb | Miltiadis Vasilakis | } else { |
224 | 700184fb | Miltiadis Vasilakis | CFStringAppendCharacters((CFMutableStringRef)json, &uc, 1); |
225 | 700184fb | Miltiadis Vasilakis | } |
226 | 700184fb | Miltiadis Vasilakis | break; |
227 | 700184fb | Miltiadis Vasilakis | |
228 | 700184fb | Miltiadis Vasilakis | } |
229 | 700184fb | Miltiadis Vasilakis | } |
230 | 700184fb | Miltiadis Vasilakis | } |
231 | 700184fb | Miltiadis Vasilakis | |
232 | 700184fb | Miltiadis Vasilakis | [json appendString:@"\""]; |
233 | 700184fb | Miltiadis Vasilakis | return YES; |
234 | 700184fb | Miltiadis Vasilakis | } |
235 | 700184fb | Miltiadis Vasilakis | |
236 | 700184fb | Miltiadis Vasilakis | |
237 | 700184fb | Miltiadis Vasilakis | @end |