İPhone'da JSON ve Temel Veriler


93

Bir temel veri nesnesi grafiğim var (bir çok ilişkisiyle birbirine bağlanan iki varlıktan oluşan).

Nispeten deneyimsiz bir iPhone geliştiricisi olarak, birinin bir yaklaşımı ve iPhone için uygun bir JSON uygulamasını önerip öneremeyeceğini merak ediyordum, bu da bana şunları yapmamı sağlar:

  1. çekirdek veri kayıtlarını bir JSON dizesine dönüştürmek (varlıklar arasındaki ilişkiyi korurken); ve

  2. JSON dizesini tekrar çekirdek veri nesnelerine dönüştürün (yine varlıklar arasındaki ilişkiyi koruyarak).

Başarısız bir şekilde, bu noktada bir öğretici / kod örneği aradım, böylece herhangi bir yardım minnetle karşılanacaktı.


3
NSJSONSerialization
İOS5

Bu sorunun biraz eski olduğunu biliyorum, ancak NSJSONSerialization veya NSDictionary kullanarak JSON'a / JSON'dan nesneleri serileştirmenize / seri durumdan çıkarmanıza olanak tanıyan OSReflectionKit adlı basit bir kitaplık oluşturdum . Ayrıca Core Data nesnelerini de destekler.
Alexandre OS

Yanıtlar:


103

Öncelikle, kullanmak için bir JSON kitaplığı seçin, ben şahsen TouchJSON'u seviyorum, ancak dışarıdaki diğerleri de oldukça güzel. Karmaşık kısım, çok zor olmasa da, yönetilen nesnelerinizi dönüştürme için uygun yapılara dönüştürmektir. Bunu çok hızlı yazdım, bu yüzden bir veya iki hata olabilir :)

Aradığınız yöntemler şunlardır:

- (NSString*)jsonStructureFromManagedObjects:(NSArray*)managedObjects;
- (NSArray*)managedObjectsFromJSONStructure:(NSString*)json withManagedObjectContext:(NSManagedObjectContext*)moc;

Ve uygulama aşağıdaki gibidir:

- (NSDictionary*)dataStructureFromManagedObject:(NSManagedObject*)managedObject
{
  NSDictionary *attributesByName = [[managedObject entity] attributesByName];
  NSDictionary *relationshipsByName = [[managedObject entity] relationshipsByName];
  NSMutableDictionary *valuesDictionary = [[managedObject dictionaryWithValuesForKeys:[attributesByName allKeys]] mutableCopy];
  [valuesDictionary setObject:[[managedObject entity] name] forKey:@"ManagedObjectName"];
  for (NSString *relationshipName in [relationshipsByName allKeys]) {
    NSRelationshipDescription *description = [[[managedObject entity] relationshipsByName] objectForKey:relationshipName];
    if (![description isToMany]) {
      NSManagedObject *relationshipObject = [managedObject valueForKey:relationshipName];
      [valuesDictionary setObject:[self dataStructureForManagedObject:relationshipObject] forKey:relationshipName];
      continue;
    }
    NSSet *relationshipObjects = [managedObject objectForKey:relationshipName];
    NSMutableArray *relationshipArray = [[NSMutableArray alloc] init];
    for (NSManagedObject *relationshipObject in relationshipObjects) {
      [relationshipArray addObject:[self dataStructureForManagedObject:relationshipObject]];
    }
    [valuesDictionary setObject:relationshipArray forKey:relationshipName];
  }
  return [valuesDictionary autorelease];
}

- (NSArray*)dataStructuresFromManagedObjects:(NSArray*)managedObjects
{
  NSMutableArray *dataArray = [[NSMutableArray alloc] init];
  for (NSManagedObject *managedObject in managedObjects) {
    [dataArray addObject:[self dataStructureForManagedObject:managedObject]];
  }
  return [dataArray autorelease];
}

- (NSString*)jsonStructureFromManagedObjects:(NSArray*)managedObjects
{
  NSArray *objectsArray = [self dataStructuresFromManagedObjects:managedObjects];
  NSString *jsonString = [[CJSONSerializer serializer] serializeArray:objectsArray];
  return jsonString;
}

- (NSManagedObject*)managedObjectFromStructure:(NSDictionary*)structureDictionary withManagedObjectContext:(NSManagedObjectContext*)moc
{
  NSString *objectName = [structureDictionary objectForKey:@"ManagedObjectName"];
  NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:objectName inManagedObjectContext:moc];
  [managedObject setValuesForKeysWithDictionary:structureDictionary];

  for (NSString *relationshipName in [[[managedObject entity] relationshipsByName] allKeys]) {
    NSRelationshipDescription *description = [relationshipsByName objectForKey:relationshipName];
    if (![description isToMany]) {
      NSDictionary *childStructureDictionary = [structureDictionary objectForKey:relationshipName];
      NSManagedObject *childObject = [self managedObjectFromStructure:childStructureDictionary withManagedObjectContext:moc];
      [managedObject setObject:childObject forKey:relationshipName];
      continue;
    }
    NSMutableSet *relationshipSet = [managedObject mutableSetForKey:relationshipName];
    NSArray *relationshipArray = [structureDictionary objectForKey:relationshipName];
    for (NSDictionary *childStructureDictionary in relationshipArray) {
      NSManagedObject *childObject = [self managedObjectFromStructure:childStructureDictionary withManagedObjectContext:moc];
      [relationshipSet addObject:childObject];
    }
  }
  return managedObject;
}

- (NSArray*)managedObjectsFromJSONStructure:(NSString*)json withManagedObjectContext:(NSManagedObjectContext*)moc
{
  NSError *error = nil;
  NSArray *structureArray = [[CJSONDeserializer deserializer] deserializeAsArray:json error:&error];
  NSAssert2(error == nil, @"Failed to deserialize\n%@\n%@", [error localizedDescription], json);
  NSMutableArray *objectArray = [[NSMutableArray alloc] init];
  for (NSDictionary *structureDictionary in structureArray) {
    [objectArray addObject:[self managedObjectFromStructure:structureDictionary withManagedObjectContext:moc]];
  }
  return [objectArray autorelease];
}

Şimdi bu yinelemeli olduğundan, dikkatli olmazsanız, kalıcı mağazanızın tamamını kolayca çevirebilirsiniz. İlişkilerinize dikkat edin ve bunların yalnızca nesne ağacından "aşağı" indiğinden emin olun, böylece yalnızca çevrilmesini istediğiniz nesneleri elde edersiniz.


Bir başka mükemmel yanıt ve çok faydalı kitabınız için tekrar teşekkür ederiz! :)
Urizen

2
Merhaba Marcus. Yukarıdaki kodu henüz denedim (derlenmesini sağlamak için bazı küçük değişikliklerle ve uygulama çökene kadar yürütme süresiz olarak devam ediyor gibi görünüyor). Rahatsız ettiğim için özür dilerim ama bu sorunu çözmek için beni doğru yönü gösterebilir misiniz diye merak ettim. DatastructureFromManagedObject yönteminde yineleme ile gerçekleşmiş gibi görünüyor ...
Urizen

1
Veri yapınıza bağlıdır. Modeliniz bir döngü oluşturacaksa sonsuza kadar çalışacaktır. Veri modelinizi gözden geçirin ve bir ağaç tasarımı olduğundan emin olun veya döngüyü önlemek için yinelemeli koda mantık durakları koyun.
Marcus S. Zarra

1
Bu kodu çalıştırmayı gerçekten denediniz mi? Çok fazla hata var. dataStructureForManagedObject bile mevcut değil. Bunun bir yazım hatası olabileceğini düşündüm, ancak bunu dataStructureFromManagedObject olarak değiştirirseniz, her şey ilişki çiftleri arasında sonsuz bir şekilde geri dönerek döngüye girer. Burada fazladan bir kod mu eksik?
Chris Mitchelmore

1
Bu kod örneği iki yıl önce tarayıcıda yazılmıştır. Kopyala yapıştır değil ilham vermesi amaçlanmıştır. Sonsuz bir döngüye gelince, bu, modelinizde bir döngü olduğu ve döngüyü kırmak için uygulamanıza modele özgü mantık eklemeniz gerektiği anlamına gelir. Bunu yapmanın bu örnekte olmayan birkaç yolu vardır.
Marcus S. Zarra

12

Sadece kodun çökmesine neden olan küçük bir yazım hatasını işaret etmek istedim ve umarım bu size birkaç dakika kazandırır.

- (NSArray*)dataStructuresFromManagedObjects:(NSArray*)managedObjects {

    NSMutableArray *dataArray = [[NSArray alloc] init];
    for (NSManagedObject *managedObject in managedObjects) {
        [dataArray addObject:[self dataStructureFromManagedObject:managedObject]];
    }
    return [dataArray autorelease];
}

NSMutableArray *dataArray = [[NSArray alloc] init]; // This should be NSMutableArray

gerçekten olmalı NSMutableArray *dataArray = [[NSMutableArray alloc] init];

hepsi bu.

teşekkür ederim


10

Çekirdek Verileri Rails ile Senkronize Etmek, Temel Veri nesnelerinizi JSON'a / JSON'dan serileştirmek / seri durumdan çıkarmak için örnek kod içeren ayrıntılı bir sunumdur (Çekirdek Veri bölümü için 55. slayda atlayın). Örnek kodu, ilişkisiz oldukça basit bir model varsayıyor, ancak bence genişletmenin oldukça kolay olacağını düşünüyorum.

Sunum ayrıca dahil bazı yararlı kütüphaneler, göstericilerle birlikte, REST tabanlı web uygulaması ile senkronize Çekirdek Veri modeli sağlamaya yönelik bazı ayrıntılı anlatır ObjectiveResource ve ASIHTTPRequest . Yapmaya çalıştığınız şeyin bu olup olmadığından emin değilim, ancak Temel Veri kodu için bile bir göz atmaya değer.


7

Bir varsa NSDateyorumların birinde yukarıda da belirtildiği gibi, yönetilen nesnesinde, sen içeren nesne seri sorunları gerekir NSDate. Basit bir düzeltme, objektif-c kategorilerini kullanmaya bir JSONDataRepresentationyöntem eklemektir NSDate.

Bu iki dosyayı projenize ekleyin:

NSdate.h:

#import <Foundation/Foundation.h>

@interface NSDate (jsondatarepresentation) 

- (NSData*) JSONDataRepresentation;

@end

NSDate.m:

#import "NSDate.h"

@implementation NSDate (jsondatarepresentation)

- (NSData*) JSONDataRepresentation {
    return [[[NSNumber numberWithDouble:[self timeIntervalSince1970]] stringValue] dataUsingEncoding:NSUTF8StringEncoding];
}

@end

3

Sadece id bu soruya hızlı bir güncelleme gönderdiğini düşündüm. Marcus ve Brandon'ın Cevaplarını takip ettim ve JSON ihracatı için bunu buldum (hala TouchJSON kullanıyor):

- (NSData*)jsonStructureFromManagedObjects:(NSArray*)managedObjects
{
    NSArray *objectsArray = [self dataStructuresFromManagedObjects:managedObjects];
    NSData *jsonData      = [[CJSONSerializer serializer] serializeArray:objectsArray error:nil];
    return jsonData;
}

- (NSArray*)dataStructuresFromManagedObjects:(NSArray*)managedObjects
{
    NSMutableArray *dataArray = [[NSMutableArray alloc] init];
    for (NSManagedObject *managedObject in managedObjects) {
        [dataArray addObject:[self dataStructureFromManagedObject:managedObject]];
    }
    return dataArray;
}

- (NSDictionary*)dataStructureFromManagedObject:(NSManagedObject*)managedObject
{
    NSDictionary *attributesByName        = [[managedObject entity] attributesByName];
    NSDictionary *relationshipsByName     = [[managedObject entity] relationshipsByName];
    NSMutableDictionary *valuesDictionary = [[managedObject dictionaryWithValuesForKeys:[attributesByName allKeys]] mutableCopy];
    [valuesDictionary setObject:[[managedObject entity] name] forKey:@"ManagedObjectName"];

    for (NSString *relationshipName in [relationshipsByName allKeys]) {

        NSRelationshipDescription *description = [[[managedObject entity] relationshipsByName] objectForKey:relationshipName];

        if ([[[description userInfo] objectForKey:@"isExportable"] boolValue] == YES) {

            if (![description isToMany]) {
                NSManagedObject *relationshipObject = [managedObject valueForKey:relationshipName];
                if (relationshipObject) {
                    [valuesDictionary setObject:[self dataStructureFromManagedObject:relationshipObject] forKey:relationshipName];
                }

                continue;
            }

            NSSet *relationshipObjects        = [managedObject valueForKey:relationshipName];
            NSMutableArray *relationshipArray = [[NSMutableArray alloc] init];

            for (NSManagedObject *relationshipObject in relationshipObjects) {
                [relationshipArray addObject:[self dataStructureFromManagedObject:relationshipObject]];
            }

            [valuesDictionary setObject:relationshipArray forKey:relationshipName];

        }

    }
    return valuesDictionary;
}

İçe aktarımın çalışmasını sağlayamadım, belki de bunun Magical Record kullanmamla bir ilgisi var emin değilim, bu yüzden sadece gelen JSON akışında döngü yapıyorum ve manuel olarak nesneler oluşturuyorum ...



2

Çok iyi çalışan bu yazıya rastladım.

http://touchalicious.com/blog/2009/10/25/turn-core-data-models-into-json.html

Bu özyinelemeli olduğu için, çoktan çoğa ilişkiler kendi içlerinde döngüye devam edecek. Bunu önlemek için, Core Data modelimdeki ilişkilerin kullanıcı bilgisi sözlüğüne bir "isExportable" anahtarı ekledim. Daha sonra bu anahtarı kontrol edebilir ve anahtar olmadan ilişkiler arasında döngü yapmamayı seçebilirsiniz.

görüntü açıklamasını buraya girin

if ([property isKindOfClass:[NSRelationshipDescription class]])
    {
        NSRelationshipDescription *relationshipDescription = (NSRelationshipDescription *)property;

        if ([[[relationshipDescription userInfo] objectForKey:@"isExportable"] boolValue] == YES)
        {
            NSString *name = [relationshipDescription name];

            if ([relationshipDescription isToMany])
            {
                NSMutableArray *arr = [properties valueForKey:name];
                if (!arr)
                {
                    arr = [[NSMutableArray alloc] init];
                    [properties setValue:arr forKey:name];
                }

                for (NSManagedObject *o in [self mutableSetValueForKey:name])
                {
                    [arr addObject:[o propertiesDictionary]];
                }
            }
            else
            {
                NSManagedObject *o = [self valueForKey:name];
                [properties setValue:[o propertiesDictionary] forKey:name];
            }
        }
    }
}

1

Marcus S. Zarra, yinelemeli fikri çalışan bir versiyona getirmem için bana ilham verdi. Bu sürümde CoreData'da bir anahtar ayarlamanıza gerek yoktur ve bunu kesip projenize yapıştırabilirsiniz :-)

// MARK: - encoding and decoding CoreData entity to dictionary

func dataStructureFromManagedObject( managedObject:NSManagedObject?, parentEntity: NSEntityDescription? = nil) -> NSMutableDictionary {
    if (managedObject != nil) {
        var attributesByName: NSDictionary = managedObject!.entity.attributesByName
        var relationshipsByName: NSDictionary  = managedObject!.entity.relationshipsByName
        var valuesImmutableDictionary: NSDictionary = managedObject!.dictionaryWithValuesForKeys( attributesByName.allKeys)
        var valuesDictionary: NSMutableDictionary = valuesImmutableDictionary.mutableCopy() as NSMutableDictionary
        valuesDictionary.setObject( managedObject!.entity.name!, forKey: "ManagedObjectName")
        for relationshipNameObject in relationshipsByName.allKeys {
            var relationshipName: NSString = relationshipNameObject as  NSString
            var relationshipDescription: NSRelationshipDescription? = relationshipsByName.objectForKey( relationshipName) as? NSRelationshipDescription
            if !relationshipDescription!.toMany {
                // ono to one
                if parentEntity == nil || (relationshipDescription! as NSRelationshipDescription).destinationEntity != parentEntity! {
                    // no parent or relationship is "downward" -> object for relationship must be added
                    var relationshipObject: NSManagedObject? = managedObject!.valueForKey( relationshipName) as? NSManagedObject
                    var relationshipObjectDictionary: NSMutableDictionary = self.dataStructureFromManagedObject( relationshipObject, parentEntity: managedObject?.entity)
                    valuesDictionary.setObject( relationshipObjectDictionary, forKey: relationshipName)
                } else {
                    // relationship is "upward" -> nothing to do
                }
            } else {
                // one to many -> all objects must be added
                var relationshipObjects: NSSet = managedObject!.mutableSetValueForKey( relationshipName)
                var relationshipArray:NSMutableArray = []
                for relationshipObjectRaw in relationshipObjects {
                    var relationshipObject:NSManagedObject? = relationshipObjectRaw as? NSManagedObject
                    if relationshipObject != nil && !relationshipObject!.entity.isKindOfEntity( managedObject!.entity) {
                        relationshipArray.addObject(self.dataStructureFromManagedObject( relationshipObject, parentEntity: managedObject?.entity))
                    }
                }
                valuesDictionary.setObject( relationshipArray, forKey: relationshipName)
            }
        }
        return valuesDictionary
    } else {
        return NSMutableDictionary()
    }
}

func managedObjectFromStructure( structureDictionary: NSDictionary, moc: NSManagedObjectContext, parentObject: NSManagedObject? = nil) -> NSManagedObject {
    if structureDictionary.count > 0 {
        var objectName:NSString = structureDictionary.objectForKey( "ManagedObjectName") as NSString
        var managedObject:NSManagedObject = NSEntityDescription.insertNewObjectForEntityForName( objectName, inManagedObjectContext: moc) as NSManagedObject
        var relationshipsByName: NSDictionary  = managedObject.entity.relationshipsByName
        var realObjectStructure:NSMutableDictionary = structureDictionary.mutableCopy() as NSMutableDictionary
        realObjectStructure.removeObjectForKey( "ManagedObjectName")
        for key in realObjectStructure.allKeys {
            // search for "ManagedObjectName" relationship entrys and delete them before filling the managedObject from this structure
            for relationshipName in relationshipsByName.allKeys {
                if relationshipName as NSString == key as NSString {
                    realObjectStructure.removeObjectForKey( key)
                }
            }
        }
        managedObject.setValuesForKeysWithDictionary( realObjectStructure)
        // the main object with attributes is created. Now care about the relationships
        for relationshipName in managedObject.entity.relationshipsByName.keys {
            var description:NSRelationshipDescription = relationshipsByName.objectForKey( relationshipName) as NSRelationshipDescription
            if !description.toMany {
                // to one relationship
                if parentObject == nil || description.destinationEntity != parentObject!.entity {
                    // no parent or relationship is "downward" -> recurse structure to add
                    var childStructureDictionary:NSDictionary = structureDictionary.objectForKey( relationshipName) as NSDictionary
                    if childStructureDictionary.count > 0 {
                        // dictionary not empty -> object must be created and added
                        var childObject:NSManagedObject? = self.managedObjectFromStructure( childStructureDictionary, moc: moc, parentObject: managedObject)
                        // validateForUpdate
                        var error:NSError?
                        if !managedObject.validateForUpdate( &error) {
                            println("Error: Object not in valid state for update!!! -> \(error)")
                        } else {
                            managedObject.setValue( childObject, forKey: relationshipName as NSString)
                        }
                    } else {
                        // relationship is "upward" -> nothing to do
                    }
                }
            } else {
                // to many relationship
                var relationshipSet:NSMutableSet = managedObject.mutableSetValueForKey( relationshipName as NSString)
                var relationshipArray:NSArray = structureDictionary.objectForKey( relationshipName as NSString) as NSArray
                for childStructureDictionary in relationshipArray {
                    if childStructureDictionary.count > 0 {
                        // dictionary not empty -> object must be created and added
                        var childObject:NSManagedObject = self.managedObjectFromStructure( childStructureDictionary as NSDictionary, moc: moc, parentObject: managedObject)
                        // validateForUpdate
                        var error:NSError?
                        if !managedObject.validateForUpdate( &error) {
                            println( "Error: Object not in valid state for update!!! -> \(error)")
                        } else {
                            relationshipSet.addObject( childObject)
                        }
                    } else {
                        // no object was behind the relationship -> nothing to do
                    }
                }
                // save set
                managedObject.setValue( relationshipSet, forKey: relationshipName as NSString)
            }
        }
        // final check validateForUpdate
        var error:NSError?
        if !managedObject.validateForUpdate( &error) {
            println( "Error: Object not in valid state for update although all previous check are passed!!! -> \(error)")
        }
        return managedObject
    } else {
        println( "Error: structure for object was empty. this should not happen at this point")
        var objectName:NSString = structureDictionary.objectForKey( "ManagedObjectName") as NSString
        var managedObject:NSManagedObject = NSEntityDescription.insertNewObjectForEntityForName( objectName, inManagedObjectContext: moc) as NSManagedObject
        return managedObject
    }
}

func dataStructuresFromManagedObjects( managedObjects: NSArray) -> NSArray {
    var dataArray:NSMutableArray = []
    for managedObject in managedObjects {
        dataArray.addObject( self.dataStructureFromManagedObject(managedObject as? NSManagedObject))
    }
    return dataArray
}

Buradaki anahtar, ana varlığı özyinelemeye argüman olarak iletmektir, böylece hangi ilişkiyi verilerle doldurmamız gerektiğine karar verebiliriz. Böylece her iki işlev de: dataStructureFromManagedObjectve managedObjectFromStructureCoreData'daki herhangi bir varlık nesnesini bir sözlüğe ve ardından bir nesneye geri kodlayabilir ve kodunu çözebilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.