2012-09-04 31 views
1

我試圖從Web服務解析JSON到核心數據。我已經看過RSAPI庫,但我無法真正理解如何在我的情況下使用它。我到目前爲止使用的JSON數據是:json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; NSData *data = [NSData dataWithContentsOfURL:url];,然後在我的tableview中將它顯示爲一個數組。有關如何將JSON數據保存到我的自定義實體的核心數據的任何建議?使用RSAPI將JSON保存到核心數據

回答

0

的易/危險方法

可可規定,使得這個非常非常簡單的方法。由於鍵 - 值編碼有在NSObject的方法,該方法減少了上面一行:

[myObj setValuesForKeysWithDictionary:jsonDict];

這將通過在jsonDict鍵/值對運行和設置MyObj中相同的鍵/值對。唯一需要注意的是密鑰名稱必須匹配,但假設您在數據模型中將它們命名爲相同,這應該不是問題。

其實還有一個警告,它是一個biggie。在此方法中,字典中包含的關鍵字確定在受管理對象上使用哪些關鍵字。但是如果字典中包含的關鍵字不是實體描述的一部分呢?然後你的應用程序崩潰,出現關於對象如何不符合鍵的鍵值編碼的錯誤。這裏使用這種方法可以讓您的應用程序的穩定性受到您所使用的Web服務的支配。如果您對Web服務沒有絕對的控制權,那麼您在這裏使用這種方法會遇到嚴重的風險。我們可以做得更好。

檢查管理對象

由於我們使用的核心數據,我們可以使用NSEntityDescription檢查被管理對象的屬性,把上面繞邏輯。我們可以使用託管對象,而不是使用傳入字典來確定要使用的鍵/值對。

您可以通過詢問其實體來查找託管對象的實體描述。這給你一個NSEntityDescription,然後你可以詢問各種有用的信息,例如存在的屬性。從JSON轉換時會導致一個更安全的方法:在實體上的屬性

NSDictionary *attributes = [[myObj entity] attributesByName]; 
for (NSString *attribute in attributes) { 
id value = [jsonDict objectForKey:attribute]; 
if (value == nil) { 
    continue; 
} 
[myObj setValue:value forKey:attribute]; 
} 

此代碼遍歷,查找字典中爲他們每個人的關鍵,並應用鍵/值對被管理對象。這仍然很好,也是通用的,並且具有改變傳入數據不會使應用程序崩潰的優點。它僅在實體描述中實際存在的屬性中查找字典中的值。額外的字典鍵被簡單地忽略。

對於nil的檢查是存在的,因爲代碼確實擊中了實體的所有屬性。如果實體具有任何傳入數據中不存在的屬性(例如「收藏夾」標誌),則代碼將最終爲該屬性設置一個零值。這可能會導致意外清除您確實想保留的數據。

處理而破裂和不一致的JSON

JSON標準是關於如何區分numbers-串基本上,串用引號括起來和數字還不太清楚。但是,JSON Web服務並不總是符合這個要求。即使是這樣,它們從一個記錄到另一個記錄並不總是一致的。

一個例子,類似於我最近遇到的一個例子:服裝的尺寸信息。尺寸由Web服務提供,通常類似於「30-32」,「34-36」等。這些在JSON中被正確引用爲字符串,並且應用將它們保存爲字符串屬性並將它們顯示給用戶原樣。

但是有時候這個大小隻有一個數字,例如, 「8」,「10」等。在這種情況下,服務器放棄引號,使它們成爲數字。我的JSON解析器正確地生成一個NSNumber。只有我想保存在我的實體的字符串屬性!我可以使用Objective-C introspection來查看我是否從我的JSON解析器收到NSString或NSNumber,但我也需要知道託管對象對於該屬性的期望類型。我簡單地考慮過打破我的JSON解析器,以便它總是返回NSString,所以至少我會知道對它的期望。幸運的是NSEntityDescription又來了。

除了詢問實體描述的屬性名稱,還可以查詢在覈心數據模型中配置的屬性類型。這是作爲NSAttributeType返回的。使用這個,我們可以擴展上面的代碼來處理不匹配的數據類型。這可能是一個好主意,抽象爲簡單的重用,太代碼,所以我把它放在一個類別上NSManagedObject:

@implementation NSManagedObject (safeSetValuesKeysWithDictionary) 



- (void)safeSetValuesForKeysWithDictionary:(NSDictionary *)keyedValues 

{ 
    NSDictionary *attributes = [[self entity] attributesByName]; 
    for (NSString *attribute in attributes) { 
     id value = [keyedValues objectForKey:attribute]; 
     if (value == nil) { 
      // Don't attempt to set nil, or you'll overwite values in self that aren't present in keyedValues 
      continue; 
     } 
     NSAttributeType attributeType = [[attributes objectForKey:attribute] attributeType]; 
     if ((attributeType == NSStringAttributeType) && ([value isKindOfClass:[NSNumber class]])) { 
      value = [value stringValue]; 
     } else if (((attributeType == NSInteger16AttributeType) || (attributeType == NSInteger32AttributeType) || (attributeType == NSInteger64AttributeType) || (attributeType == NSBooleanAttributeType)) && ([value isKindOfClass:[NSString class]])) { 
      value = [NSNumber numberWithInteger:[value integerValue]]; 
     } else if ((attributeType == NSFloatAttributeType) && ([value isKindOfClass:[NSString class]])) { 
      value = [NSNumber numberWithDouble:[value doubleValue]]; 
     } 
     [self setValue:value forKey:attribute]; 
    } 
} 
@end 

此代碼檢查中,我們創建從字典中找到的類型都值傳入JSON和管理對象上的屬性,如果有字符串/數字不匹配,它會修改該值以確保它與預期的匹配。

這變得非常有用。它不僅是一個通用的JSON到NSManagedObject的轉換,它還處理數字和字符串類型之間的不匹配,而不需要任何特定於實體的信息。不過,這可能會更好。 處理日期

JSON沒有日期類型,但日期在JSON中很常見。它們只是使用一種日期格式或其他格式表示爲字符串。只有你可能需要一個NSDate,而不是一個字符串。 NSDateFormatter在這裏真的很有用,但是將日期轉換爲通用也不是件好事,因此您不需要特殊處理日期屬性?你能看到我要去的地方嗎?

寫了上面的類別方法後,添加一個可選的NSDateFormatter參數並沒有太多工作,然後在實體的屬性需要日期時使用它。這個修改後的版本增加了這個參數,並在「else ... if」鏈中增加了一個例子:

@implementation NSManagedObject (safeSetValuesKeysWithDictionary) 

- (void)safeSetValuesForKeysWithDictionary:(NSDictionary *)keyedValues dateFormatter:(NSDateFormatter *)dateFormatter 
{ 
    NSDictionary *attributes = [[self entity] attributesByName]; 
    for (NSString *attribute in attributes) { 
     id value = [keyedValues objectForKey:attribute]; 
     if (value == nil) { 
      continue; 
     } 
     NSAttributeType attributeType = [[attributes objectForKey:attribute] attributeType]; 
     if ((attributeType == NSStringAttributeType) && ([value isKindOfClass:[NSNumber class]])) { 
      value = [value stringValue]; 
     } else if (((attributeType == NSInteger16AttributeType) || (attributeType == NSInteger32AttributeType) || (attributeType == NSInteger64AttributeType) || (attributeType == NSBooleanAttributeType)) && ([value isKindOfClass:[NSString class]])) { 
      value = [NSNumber numberWithInteger:[value integerValue]]; 
     } else if ((attributeType == NSFloatAttributeType) && ([value isKindOfClass:[NSString class]])) { 
      value = [NSNumber numberWithDouble:[value doubleValue]]; 
     } else if ((attributeType == NSDateAttributeType) && ([value isKindOfClass:[NSString class]]) && (dateFormatter != nil)) { 
      value = [dateFormatter dateFromString:value]; 
     } 
     [self setValue:value forKey:attribute]; 
    } 
} 
@end