2011-05-26 146 views
12

我想構建一個解析器/ objectMapper,它將爲我從REST服務使用的JSON構建Objective C對象。從JSON到NSObject的對象映射庫

我從RestKit中獲得了一些靈感,讓我的實體全部擁有一個「解碼列表」,它告訴映射器哪些JSON鍵與哪些對象一起使用。像這樣:

//ObjectEntity implementation 
+ (NSDictionary*) mapProperties { 

    /* 
    localPropertiy - JSONProperty 
    */ 

    return @{ 
      @"name": @"name", 
      @"category": @"category", 
      @"possible_scopes": @"possibleScopes", 
      @"possible_descriptions": @"possibleDescriptions", 
      @"key": @"keys"    
    }; 

} 

+ (NSDictionary*) mapRelations { 

    return [NSDictionary dictionary]; 
} 

我這樣做是因爲我喜歡將這些可更改值封裝在它們引用的對象中。儘可能少地使Mapper知道。

映射器確實是這樣的:

+ (NSArray*) parseData:(NSData*) jsonData intoObjectsOfType:(Class) objectClass { 

    //Parser result from web service 
    NSError *error = nil; 
    CJSONDeserializer *deserializer = [CJSONDeserializer deserializer]; 
    [deserializer setNullObject:nil]; 
    NSArray *objects = [deserializer deserializeAsArray:jsonData error:&error]; 

    NSMutableArray *result = [NSMutableArray array]; 

    for (NSDictionary *o in objects) { 

     id <EntityProtocol> entity = [[objectClass alloc] init]; 

     NSDictionary *jsonKeys = objectClass.mapProperties; 

     for (NSString *key in jsonKeys.allKeys) { 

      NSString *objectProperty = jsonKeys[key]; 
      NSString *value = o[key]; 
      if (value) 
       [entity setValue:value forKey:objectProperty]; 
     } 

     [result addObject:entity]; 

    } 

    return (NSArray*)result; 
} 

所以我消息解析器/映射器這樣的:

NSArray *objects = [ObjectParser parseData:self.responseData intoObjectsOfType:ObjectEntity.class]; 

這意味着解析器必須知道我的根對象是什麼,這是很好,因爲從Web服務中檢索它的對象當然有這方面的知識。

上面只適用於沒有嵌套對象的JSON,我一直在試圖構建解析器,以便它也將需要考慮的關係,建立所需的對象並將它們插入根對象,這需要遞歸和我一直跑到死衚衕。

我想要一些幫助,以便我可以如何處理這個或任何洞察力,就好像這樣的事情存在於圖書館中一樣。也許是爲了使用或者只是爲了解決我遇到的問題。

預先感謝您。

+1

+1因爲你正在做一些很酷的事情! – 2011-05-26 11:01:23

+0

謝謝亞歷克,看起來很酷的事情現在很難弄清楚。 – RickiG 2011-05-30 21:38:21

+0

我們有一個相當複雜的發送/接收JSON的設置,但其要點是所有啓用JSON的類都有一個'initWithDict'方法和一個'asDictionary'方法。每個類都負責讀取/寫入它擁有的數據,然後調用組件類的相同方法。 – 2014-01-29 18:04:44

回答

2

爲什麼不爲類添加映射?

+ (NSDictionary *)mapClasses { 
    return @{ 
      @"category": [Category class], 
      // ... 
    }; 
} 

對於非容器的屬性,你甚至可以做runtime introspection of properties避免多餘的映射。

集裝箱特性可以映射到特殊的包裝對象:

[OMDynamicType typeWithBlock:^(id obj){ 
    if ([obj isKindOfClass:NSString.class] && [obj hasPrefix:@"foo"]) 
     return Foo.class; 
    else 
     return Bar.class; 
}], @"foo", 

實現這個可以去像:

+ (NSArray *)parseData:(NSData*)jsonData intoObjectsOfType:(Class)objectClass { 
    NSArray *parsed = /* ... */ 
    NSMutableArray *decoded = [NSMutableArray array]; 
    for (id obj in parsed) { 
     [decoded addObject:[self decodeRawObject:parsed intoObjectOfType:objectClass]]; 
    } 
    return decoded; 
} 

+ (id)decodeRawObject:(NSDictionary *)dict intoObjectOfType:(Class)objectClass { 
    // ... 
    NSDictionary *jsonKeys = objectClass.mapProperties; 
    NSDictionary *jsonClasses = objectClass.mapClasses; 

    for (NSString *key in jsonKeys.allKeys) { 
     NSString *objectProperty = jsonKeys[key]; 
     NSString *value = dict[key]; 
     if (value) { 
      id klass = jsonClasses[key]; 
      if (!klass) { 
       [entity setValue:value forKey:objectProperty]; 
      } else if (klass == klass.class) { 
       [entity setValue:[self decodeRawObject:value intoObjectOfType:klass] 
          forKey:objectProperty]; 
      } else if (/* check for containers and blocks */) { 
       // ... 
      } 
     } 
    } 
    // ... 
} 
+0

謝謝威爾伯!非常好的東西,足以讓我去。我喜歡你在字典中返回一個類的想法,可以很容易地進行反思。 – RickiG 2011-05-30 21:37:27

10

的類型動態選擇

[OMContainer arrayWithClass:Key.class], @"keys", 
[OMContainer dicionaryWithKeyClass:ScopeID.class valueClass:Scope.class], @"possibleScopes", 

甚至阻擋考慮使用RestKit:http://restkit.org

這個框架包含了你所需要的一切 - REST和JSON抽象,對象映射,甚至核心數據支持以及大量真正有用的東西,所有這些都以可定製和優雅的方式實現。

UPDATE:好的,在編寫另一種映射方法時,我決定我不能再做了,並做了一個小框架。它反省了對象的屬性,並通過一些調整爲您提供免費的非常詳細的描述,isEqual/hashCode,免費的NSCoding支持,並允許從JSON映射器(實際上,NSDictionary,但其他人會使用它)生成。所有NSNull檢查,JSON中缺少字段,JSON中新的意外字段都會得到適當處理並正確報告。

如果有人希望這個共享給公衆,你可以給我一些upvotes或評論。我最終會這樣做,但我可能會考慮更快地分享它。

+0

現在一直使用它半年:)是強烈推薦! – RickiG 2012-01-25 11:24:37

+0

我發現它很難安裝。在ASIHTTPRequests停止支持後,我更不願意依賴更大的框架。 – zekel 2012-05-03 21:22:25

0

試用CSMapper,真是太神奇了!您只需創建與該類命名相同的plists,映射一些屬性,然後使用一行代碼輕鬆地將字典映射到您的對象。我已經在很多項目上測試過它,發現它非常乾淨,性能卓越。它使我能夠靈活地輕鬆地在開發生命週期中響應API更改。

https://github.com/marcammann/CSMapper

我目前正在更新的文件和添加到在個人叉的項目,應該快了合併:)

https://github.com/AntonTheDev/CSMapper

1

我也嘗試過同樣的方法。我的主要問題是,準確地描述類型。例如,對於嵌套數組我用這樣的:

@{ @"friends" : @[[Person class]] } 

但事情變得凌亂,因爲我需要創造更多特殊的語法不同類型,我想支持。最後,我停止了追求這種方法並尋求另一種解決方案 - 也因爲我需要做的運行時檢查會減慢轉換速度。

現在有很多解決方案。我建議看看Awesome iOS網站。我也想指出JSON Class Generator,因爲它可以讓你

  • 準確(對日期等特殊映射器在內)描述你的類型,
  • 類型的自動檢查(不匹配報告和故障預置值提供),
  • 你不需要花時間寫重複的代碼(和生成的代碼似乎總是「只是工作」)

我不想做廣告,但是這個工具爲我節約了很多工作,我投入到我的應用程序的其他功能秒。它出現了一點遲,現在世界似乎切換到Swift,但更有理由不浪費時間編寫Objective-C模型。

下面是一些示例代碼轉換爲/形式JSON:

// converting MyClass <-> NSDictionary 
MyClass  *myClass = [MyClass myClassWithDict:someJsonDictionary]; 
NSDictionary *jsonDict = [myClass toDict]; 

// converting NSDictionary <-> NSData 
NSDictionary *someJsonDictionary = [NSDictionary api_dictionaryWithJson:someJsonData]; 
NSData *jsonData = [someJsonDictionary api_toJson]; 

在之前的評論中提及的特徵也被JSON Class Generator支持:

免費
-isEqual, -hash, -description, -copy, NSCoding/NSSecureCoding/NSKeyedArchiver 

支持,並檢查缺失/ additional/NSNull /可選字段和JSON響應中的錯誤類型,報告錯誤,正常使用fallback值失敗,並使用方法調度而不是運行時類型自省(這更快)執行此操作。