【问题标题】:Setting an object's properties from an NSDictionary with keys that are not identical to the property names使用与属性名称不同的键从 NSDictionary 设置对象的属性
【发布时间】:2025-11-23 15:50:01
【问题描述】:

我需要根据远程服务器通过 JSON 提供的NSDictionary 设置一组对象属性。我不想覆盖字典中没有的属性。

由于属性很多,我有一长串类似这样的语句:

if (dictionary[@"address_1"] != [NSNull null]) 
    self.configuration.userAddress1 = dictionary[@"address_1"];

字典中的键与属性的名称不同;有两个不同的系统是分开成长的,我试图让它们一起工作。

也许我一直在做太多的 Ruby 编码,但似乎在 Objective-C 中应该有一个更好的习惯用法来做这件事。有什么想法吗?

【问题讨论】:

  • 我应该提到属性名称和字典键名称并不完全相同:例如 userAddress1 vs address_1。那是因为它们是两个不同的系统,分别长大,试图一起工作。所以我在这个过程中做了一些“翻译”。
  • 这是非常重要的一点,因为如果它不是真的,这就像using setValuesForKeysWithDictionary: 一样简单。但是,如果您转换字典键 first.,您仍然可以使用它
  • 嗯。现在这是一个想法。
  • 感谢所有的想法,但正如一些人在下面指出的那样,治愈可能比疾病更糟糕。我会坚持以上。

标签: objective-c cocoa cocoa-touch nsdictionary


【解决方案1】:

听起来您想要一个简单的地图解决方案 - 您可以像这样手动滚动一个

[@{
   @"address_1" : @"address1",
   @"address_2" : @"address2",
   ...
   } enumerateKeysAndObjectsUsingBlock:^(NSString *remoteKey, NSString *localKey, BOOL *stop) {
     id remoteValue = dictionary[remoteKey];

     if (![remoteValue isEqual:NSNull.null]) {
       [self.configuration setValue:remoteValue forKey:localKey];
     }
   }];

这应用了一些基本的Null 检查逻辑,并允许远程/本地对象具有不同的属性名称

【讨论】:

  • 我对此投了赞成票,只是因为这是一个值得记住的好主意。谢谢!
【解决方案2】:

不,这不是 ruby​​ish,因为 Objective-C 是动态类型的。您可以使用键值编码来做到这一点:

for (NSString *key in dictionary)
{
    id value = dictionary[key];
    [self.configuration setValue:value forKey:key];
}

但是看我的 cmets here.

顺便说一句:如果字典中不存在某个键,则结果是 nil 而不是 [NSNull null]

因此,如果您不想设置字典中没有的属性,则无需执行任何其他操作。如果您不想设置字典中值为[NSNull null] 的属性,您仍然可以添加检查。

如果你不想设置空值:

for (NSString *key in dictionary)
{
  id value = dictionary[key];
  if (value != [NSNull null] )
  {
    [self.configuration setValue:value forKey:key];
  }
}

如果你确实想用 nil 设置 null:

for (NSString *key in dictionary)
{
  id value = dictionary[key];
  if (value == [NSNull null] )
  {
    value = nil;
  }
  [self.configuration setValue:value forKey:key];
}

【讨论】:

  • 但通过 JSON 解析创建的字典包含 NSNull 值的情况并不少见。看来 OP 想要检查 NSNull 值并采取相应措施。
  • 不,这并不少见,但他说:“我不想覆盖字典中没有的属性。”他没有说:“我不想覆盖字典中带有null 的属性。” (并且可能,如果字典中有一个值为[NSNull null] 的键,您希望将该属性设置为nil。)但我添加了一个说明。
  • 是的,我测试 [NSNull null] 的原因是因为字典确实包含那种 null。测试 nil 不起作用。另一个复杂之处是字典中的键名和属性名并不完全相同(出于历史原因)。
  • 是的,nilNSNull 是非常不同的东西,@John。 nil 字面意思是“没有对象”:(id)NULLNSNull 一个对象,但它是 Cocoa 集合的“无价值”占位符。
  • @John 这就是为什么我将我的 cmets 链接到另一个 Q 的原因。一个通用的解决方案在开始时非常性感。但是你必须保留一个字典,因为键改变了,你必须检查键的值,你在一个你可以拥有它的地方失去了深度的安全性......在一天结束时,你发现自己定义了一个一种DSL。不值得。
【解决方案3】:

Objective-C 中一个可能的习惯用法是:不要有很多对象属性。有一个属性,一本字典!现在很容易根据传入的字典填充该字典。

【讨论】:

    【解决方案4】:

    另一种方法是在您的类中覆盖 -setValue:forUndefinedKey: 并将键映射到那里。

    - (void) setValue:(id)value forUndefinedKey:(NSString*)key
    {
        static dispatch_once_t once;
        static NSDictionary* map;
        dispatch_once(&once, ^{
            map = @{
                     @"address_1" : @"address1",
                     @"address_2" : @"address2",
                     // ...
                   };
        });
    
        NSString* newKey = map[key];
        if (newKey)
            [self setValue:value forKey:newKey];
        else
            [super setValue:value forUndefinedKey:key];
    }
    

    现在您可以将-setValuesForKeysWithDictionary: 与原始字典一起使用。该方法会将nil 替换为NSNull 对象,因此您必须确定这是否是您想要的。

    如果需要,您也可以通过覆盖 -valueForUndefinedKey: 来实现另一个方向。

    【讨论】:

      最近更新 更多