sogo/SoObjects/SOGo/NSDictionary+BSJSONAdditions.m
Wolfgang Sourdeau 7f19ee2036 Monotone-Parent: 680b3d6bcb1de20bb28768ebf727ed9053b9e2a0
Monotone-Revision: e94c8ae67d2887b188febc57c064c3e5be325bc9

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2007-12-13T18:54:27
Monotone-Branch: ca.inverse.sogo
2007-12-13 18:54:27 +00:00

199 lines
6.7 KiB
Objective-C

//
// 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 <Foundation/NSArray.h>
#import <Foundation/NSDecimalNumber.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSNull.h>
#import <Foundation/NSValue.h>
#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];
[scanner release];
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 autorelease];
}
- (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 autorelease];
}
- (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 autorelease];
}
- (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 autorelease];
}
@end