CollectionFactory 1.3.0

CollectionFactory 1.3.0

测试已测试
语言语言 Obj-CObjective C
许可证 MIT
发布时间最后发布2016年10月

Elliot Chance维护。



  • 作者
  • Elliot Chance

Objective-C 中本地集合与 JSON 之间的转换。

静态方法在发生错误时(如 JSON 无法传递、为 nil 或无效的预期类型)总是返回 nil

转换为 JSON

原生类型

您可以使用 JSONStringJSONData 分别获取 JSON 编码的 NSString 或 NSData 版本。

NSDictionary *d = @{@"foo": @"bar"};

// {"foo":"bar"}
NSString *JSONString = [d JSONString];

// The same value as above but as a NSData
NSData *JSONData = [d JSONData];

这两种方法都适用于 NSNullNSNumberNSArrayNSDictionaryNSObjectNSString

自定义类型

您也可以转换任何 NSObject 的子类

@interface SomeObject : NSObject

@property NSString *string;
@property int number;

@end
SomeObject *myObject = [SomeObject new];
myObject.string = @"foo";
myObject.number = 123;

// {"string":"foo","number":123}
NSString *json = [myObject JSONString];

如果您需要控制自定义对象的序列化方式,可以重写 [JSONDictionary] 方法

@implementation SomeObject

- (NSDictionary *)JSONDictionary
{
    return @{
        @"number": self.number,
        @"secret": @"bar",
    };
}

@end
SomeObject *myObject = [SomeObject new];
myObject.string = @"foo";
myObject.number = 123;

// {"number":123,"secret":"bar"}
NSString *json = [myObject JSONString];

从 JSON 转换

原生类型

将 JSON 转换为对象的最简单方法是将其通过 NSObject 运行

NSString *json = @"{\"foo\":\"bar\"}";
id object = [NSObject objectWithJSONString:json];

但是,如果您知道传入值的类型,应使用相应的类工厂(而不是盲目地转换)

NSString *json = @"{\"foo\":\"bar\"}";
NSDictionary *d = [NSDictionary dictionaryWithJSONString:json];

当使用特定类时,它不会接受有效 JSON 值的意外类型,以防止出现错误,例如

NSString *json = @"{\"foo\":\"bar\"}";

// `a` is `nil` because we only intend to decode a JSON array.
NSArray *a = [NSArray arrayWithJSONString:json];

// `b` is an instance of `NSDictionary` but future code will be treating it like
// an `NSArray` which will surely cause very bad things to happen...
NSArray *b = [NSObject objectWithJSONString:json];

自定义类型

假设您有这个

@interface SomeObject : NSObject

@property NSString *string;
@property int number;

@end

与解包原生类型的相同方法,只不过因为静态方法 [objectWithJSONString:] 是针对 SomeObject 调用的,所以您正在表示必须反序列化为此类型的对象。

NSString *json = @"{\"string\":\"foo\",\"number\":123};

SomeObject *myObject = [SomeObject objectWithJSONString:json];

// 123
myObject.number;

// Do NOT do this. Otherwise you will get an NSDictionary.
// SomeObject *myObject = [NSObject objectWithJSONString:json];

对象递归构造,首先检查属性是否存在,如果存在且数据不以 NS 前缀,则将创建另一个自定义对象并继续。这意味着可以像使用任何具体代码一样使用 JSON 来解包简单对象,但这也有些限制

  1. 这是一个危险的操作。并非所有属性都是公开的,甚至可能不存在,所以很容易缺失类型,在使用解包后的对象时可能导致严重的内存错误。

  2. 它始终使用 [init] 构造函数,这可能是错误的,甚至可能不可用,使得对象构造变得不可能。

解包对象的一个更安全的做法是重写 [setValue:forProperty:] 方法。这允许您准确控制每个属性的所需逻辑。

注意:这是一个对 [setValue:forKey:] 的包装,如果未重写它,则会调用 [setValue:forKey:]

@implementation SomeObject

- (void)setValue:(id)value forProperty:(NSString *)key
{
    // Only allow these two properties to be set.
    NSArray *properties = @[@"number", @"string"];
    if ([properties indexOfObject:key] != NSNotFound) {
        [self setValue:value forKey:key];
    }
}

@end

美化打印

默认情况下,序列化的JSON不会包含额外空格,这对传输和存储最佳,但对调试来说可读性较差。您可以通过指定缩进大小生成具有美化的JSON。

NSDictionary *object = @{@"abc": @"def"};

// {
//   "abc": "def"
// }
NSString *result = [object prettyJSONStringWithIndentSize:2];

创建可变对象

对于每个工厂方法都有一个可变对应物,用于生成可以在解包后安全的直接编辑的对象。

NSString *json = @"{\"foo\":\"bar\"}";
NSDictionary *d = [NSDictionary dictionaryWithJSONString:json];
NSMutableDictionary *md = [NSMutableDictionary mutableDictionaryWithJSONString:json];

从文件加载

每个工厂方法都有一个直接从文件生成对象的方式。

NSArray *foo = [NSArray arrayWithJSONFile:@"foo.json"];

如果文件不存在、解析出错或JSON类型错误,则返回 nil

此外,您还可以从文件创建可变对象。

NSMutableArray *foo = [NSMutableArray mutableArrayWithJSONFile:@"foo.json"];

处理错误

在大多数情况下,您不会遇到问题。然而,默认行为是返回 nil 并隐藏错误原因。如果您想要更安全地处理错误,可以提供一个指向 NSError 的引用。

NSError *error;
NSString *result = [object JSONStringOrError:&error];
if (error) {
    // Use these:
    //   error.localizedDescription
    //   error.localizedFailureReason
}

这对于 JSONDataOrError:JSONDictionaryOrError: 都是一样的。