【问题标题】:How to serialize a custom object in NSDictionary using JSON?如何使用 JSON 序列化 NSDictionary 中的自定义对象?
【发布时间】:2012-06-04 05:53:16
【问题描述】:

我有以下代码:

NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:@"one", @"oneKey", 
@"two", @"twoKey", customObject, @"customObjectKey", nil];
if([NSJSONSerialization isValidJSONObject:dict])
{
    NSLog(@"Went through.");
}

如果对象是 NSString 的,它会通过,但是一旦我将 customObject 添加到字典中,它就不再有效。我该如何解决?任何帮助深表感谢。提前致谢!

【问题讨论】:

    标签: ios5 nsdictionary nsjsonserialization custom-object


    【解决方案1】:

    简单的事实是:你不能。阅读 NSJSONSerialization 的文档,其中没有列出 NSCoding。 :(

    【讨论】:

      【解决方案2】:

      您不能将自定义对象设置为字典的键。确定,你的键是什么 NSStrings

      来自 Foundatoin.framework 的文本在 func +(BOOL) isValidJSONObject:(id)obj;

      - All dictionary keys are NSStrings
      

      【讨论】:

      • 没错,但他并没有试图将它用作钥匙。他试图将其用作价值。
      【解决方案3】:

      原因:这是因为“customObject”不是:NSArray、NSDictionary 或 NSString。很可能是用户定义的类,这是 isValidJSONOperation 的约定:

      /* 如果给定对象可以转换为 JSON 数据,则返回 YES,否则返回 NO。该对象必须具有以下属性:
          - 顶级对象是 NSArray 或 NSDictionary
          - 所有对象都是 NSString、NSNumber、NSArray、NSDictionary 或 NSNull
          - 所有字典键都是 NSStrings
          - NSNumbers 不是 NaN 或无穷大
       其他规则可能适用。调用此方法或尝试转换是判断给定对象是否可以转换为 JSON 数据的确定方法。
       */
      + (BOOL)isValidJSONObject:(id)obj;

      可能的解决方案 1:在您的 customObject 类中添加另一个类型为 NSDictonary 的属性,该属性由示例字典调用,该字典包含要添加到 JSON 中的属性。

      NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:@"one", @"oneKey",
      @"two", @"twoKey", customObject.dictionary, @"customObjectKey", nil];

      可能的解决方案 2:使用要从 customObject 添加到 JSON 中的属性(和值)创建一个新字典。

      【讨论】:

      • "这是因为 "customObject" 不是:NSArrayNSDictionary 也不是 NSString" ...正如您在后面的引文中所指出的,NSNumberNSNull 也会是 JSON 中可接受的值。
      【解决方案4】:

      键是字符串,但值可以是任何对象,包括 NSNull。

      【讨论】:

      • 不,这是不正确的。 NSJSONSerialization class reference 非常具体,“可以转换为 JSON 的对象必须具有以下属性:(a) 顶级对象是 NSArrayNSDictionary。(b) 所有对象都是 @987654325 的实例@、NSNumberNSArrayNSDictionaryNSNull。(c) 所有字典键都是 NSString 的实例。(d) 数字不是 NaN 或无穷大。"所以它不能是“任何”对象,而只能是这五个类中的一个。
      【解决方案5】:

      “如何在 XCode 中使用 JSON 序列化 NSDictionary 中的自定义对象?”

      找出我讨厌 XCode 的另一个原因,希望有人能把它从 1990 年代拖出来。

      让我们通过一个示例来说明我们应该如何序列化自定义对象。

      假设您有一个非常简单的 UserRecord 类,其 .h 文件如下:

      @interface UserRecord : NSObject
      
      @property(nonatomic) int UserID;
      @property(nonatomic, strong) NSString* FirstName;
      @property(nonatomic, strong) NSString* LastName;
      @property(nonatomic) int Age;
      
      @end
      

      还有一个像这样的 .m:

      @implementation UserRecord
      
      @synthesize UserID;
      @synthesize FirstName;
      @synthesize LastName;
      @synthesize Age;
      
      @end
      

      如果您尝试创建一个 UserRecord 对象,并使用 NSJSONSerialization 类对其进行序列化..

      UserRecord* sampleRecord = [[UserRecord alloc] init];
      sampleRecord.UserID = 13;
      sampleRecord.FirstName = @"Mike";
      sampleRecord.LastName = @"Gledhill";
      sampleRecord.Age = 82;
      
      NSError* error = nil;
      NSData* jsonData2 = [NSJSONSerialization dataWithJSONObject:sampleRecord options:NSJSONWritingPrettyPrinted error:&error];
      

      ..它会嘲笑你,抛出异常并使你的应用程序崩溃:

      *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[NSJSONSerialization dataWithJSONObject:options:error:]: Invalid top-level type in JSON write'
      

      解决这个闹剧的一种方法是向您的NSObject 添加一个函数,将您的数据转换为NSDictionary,然后将其序列化

      这是我班级的新 .m 文件:

      @implementation UserRecord
      
      @synthesize UserID;
      @synthesize FirstName;
      @synthesize LastName;
      @synthesize Age;
      
      -(NSDictionary*)fetchInDictionaryForm
      {
          NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
      
          [dict setObject:[NSNumber numberWithInt:UserID] forKey:@"UserID"];
          [dict setObject:FirstName forKey:@"FirstName"];
          [dict setObject:LastName forKey:@"LastName"];
          [dict setObject:[NSNumber numberWithInt:Age] forKey:@"Age"];
      
          return dict;
      }
      
      @end
      

      实际上,如果您愿意,您可以一次性创建 NSDictionary 值:

      -(NSDictionary*)fetchInDictionaryForm
      {
          NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                               [NSNumber numberWithInt:UserID], @"UserID",
                               FirstName, @"FirstName",
                               LastName,@"LastName",
                               [NSNumber numberWithInt:Age], @"Age",
                               nil];
          return dict;
      }
      

      完成此操作后,您可以获取NSJSONSerialization 来序列化您的对象的NSDictionary 版本:

      UserRecord* sampleRecord = [[UserRecord alloc] init];
      sampleRecord.UserID = 13;
      sampleRecord.FirstName = @"Mike";
      sampleRecord.LastName = @"Gledhill";
      sampleRecord.Age = 82;
      
      
      NSDictionary* dictionary = [sampleRecord fetchInDictionaryForm];
      if ([NSJSONSerialization isValidJSONObject:dictionary])
      {
          NSError* error = nil;
          NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:&error];
          NSString* jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
      
          NSLog(@"%@", jsonString);
      }
      

      这将产生我们想要的 JSON 输出:

      {
        "UserID" : 13,
        "FirstName" : "Mike",
        "LastName" : "Gledhill",
        "Age" : 82
      }
      

      令人震惊。即使在 2015 年,Apple 的 SDK 也无法将一组简单的 ints 和 NSStrings 序列化为 JSON。

      希望这可以帮助其他 XCode 受害者。

      【讨论】:

      • Xcode 是一个 IDE。听起来你的抱怨是针对 Foundation / Objective-C 的。
      【解决方案6】:

      您需要使用NSCoding 协议来序列化自定义对象。

      在.h文件中实现NSCoding协议,在你的自定义类的.m文件中实现以下方法。

      - (id)initWithCoder:(NSCoder *)aDecoder
      {
         if(self = [super init]) // this needs to be [super initWithCoder:aDecoder] if the superclass implements NSCoding
         {
            aString = [[aDecoder decodeObjectForKey:@"aStringkey"] retain];
            anotherString = [[aDecoder decodeObjectForKey:@"anotherStringkey"] retain];
         }
         return self;
      }
      

      - (void)encodeWithCoder:(NSCoder *)encoder
      {
         // add [super encodeWithCoder:encoder] if the superclass implements NSCoding
         [encoder encodeObject:aString forKey:@"aStringkey"];
         [encoder encodeObject:anotherString forKey:@"anotherStringkey"];
      }
      

      【讨论】:

      • NSCoding 与 NSJSONSerialization 无关。它应该,但它没有。
      • @Andy 不,正如 dwegkees 指出的那样,这个答案是不正确的。这就是您如何使对象能够为存档进行编码和解码(请参阅 存档和序列化指南 中的Encoding and Decoding Objects),但这并不能使其能够与@987654326 结合使用@。这个答案是错误的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-05
      • 2014-05-19
      • 1970-01-01
      • 1970-01-01
      • 2021-06-24
      • 2014-03-03
      • 2020-07-02
      相关资源
      最近更新 更多