2

I need to store some data coming from a API call as JSON locally using CoreData.

The problem is, the JSON is too complicated to me to handle in CoreData. I’m getting JSON as a Dictionary with 4 keys, and these 4 keys again holds Dictionary's and those Dictionary's have Array's and Dictionary's.

Now, I don’t really know how I should design Entities and Attributes for this requirement, but I tried to do that and what I did is, I created a Entity (say XYZ) and this entity has 4 relations (one to one) to 4 other entities and these four entities are nothing but four Dictionary's which I have got in the JSON file. And I’m storing these Array's and Dictionary's as transformable types. It gets a little confusing, right? So let me put the JSON data here

{
        outerKey1 =         {
            someKey = “Some String";
            disableAutoFill = 1;
            disableABC = 1;
            disableXYZ = 1;
            disableThis = 1;
            disableThat = 1;
            disableBla = 1;
            disableBlaBla= 1;
            disableBlaBlaBlaBla = 1;
            disableBlaBlaBlaBlaBlaBla= 1;
        };
        outerKey2 =         {
            someKey =             (
                                {
                    markPath = 0;
                    title = "Some Name";
                    url = "http://www.BlaBla.com";
                },
                                {
                   markPath = 0;
                    title = "Some Name";
                    url = "http://www.something.com";
                },
                                {
                    markPath = 0;
                    title = Yahoo;
                    url = "http://www.yahoo.com";
                },
                                {
                   Path = 0;
                    title = “title";
                    url = "http://www.title.com";
                }
            );
            enabled = 1;
        };
        outerKey3 =         {
            enabled = 1;
            gatewayIP = "192.172.169.10";
            gatewayPort = 8080;
            gatewayRoutingUrls =             (
                "www.kuchbhi.com",
                "www.oh-teri.com"
            );
        };
        outerKey4 =         {
           SomeCategories =             (
                SomeCategories,
        someOtherCategories
            );
            defaultUrl = "www.meriapniwebsite.com";
            enabled = 1;
            exceptionUrls =             (
                "www.kuchbhihojay.com"
            );
            filterUrls =             (
                "www.kuchtobhi.com",
                "www.kyaaapjhandhai.com"
            );
            filteringFlag = 1;
        };
    }

Is my approach ok or does it need to be corrected? Or do I need to implement it in an entirely different fashion. Please help me, thanks

Suryakant Sharma
  • 3,852
  • 1
  • 25
  • 47
  • could look into using [Realm](https://realm.io) for your database, really simple to use – Fonix Feb 16 '16 at 06:52
  • 1
    @Fonix but doing so doesn't solve the question of how to model the data – Tom Harrington Feb 16 '16 at 18:19
  • @TomHarrington hence it wasnt an answer – Fonix Feb 17 '16 at 02:02
  • @TomHarrington please give me some pointer atleast. – Suryakant Sharma Feb 17 '16 at 04:59
  • as far as comparing two json responses goes... you can store hashed checksums of each one and compare them before reloading. As for the data models, you would need at least 6 models for this structure the way it looks at the first glance. I would personally go with sqlite instead of coredata, but that depends on the size of the tables themselves. Is this the right JSON file or just some rough outline, because it's not a valid one. – user2695712 Feb 24 '16 at 10:31
  • @user2695712 comparison part I have done and done with the hash thing and yes this is right JSON, I'm getting it, in the above format itself. – Suryakant Sharma Feb 24 '16 at 10:52
  • ok! it has some formatting issue, but structure is same. @user2695712 – Suryakant Sharma Feb 24 '16 at 10:58
  • I have face something like it before, start to modeling, bottom up, the inner structure will have a relation (1 to many) with the outer structure, got the idea? (inner) *---o (outer)*---o(outer)... that how you model arrays. – Ulysses Feb 24 '16 at 11:06
  • @UlyssesR I'm completely blank about array of dictionary, which I have in the JSON. how would I create entity/attributes for that? no idea – Suryakant Sharma Feb 24 '16 at 11:57
  • Take a look, http://stackoverflow.com/questions/1562676/best-practice-array-dictionary-as-a-core-data-entity-attribute – Ulysses Feb 24 '16 at 14:04
  • Can you explain how you are going to use this data in your app? For example, will you ever need to access individual properties or maybe you just need to store all of them and then output them all at once? This makes a big difference as to what is the best way to store this. – Andriy Gordiychuk Feb 25 '16 at 14:39
  • @AndriyGordiychuk, I need all the object at once, no individual property access. I need to read all the data and use those properties, all at once. – Suryakant Sharma Feb 26 '16 at 04:44
  • for complex json it is best to save the json string as a whole in database . – Reshmi Majumder Feb 29 '16 at 05:36

3 Answers3

7

The problem is that you are trying to create your CoreData entities based on the structure of your JSON. You should be creating your entities based on the objects that your JSON represents and the relationships between them.

Your question doesn't really give enough information about the intent of your data model for me to give you an exact answer, but it's likely that storing this data as a bunch of transformable dictionaries isn't the best answer. Using transformable properties means that none of those values can be queried, and you don't have an explicit list of the properties that should be in those dictionaries. This means you are going to have a ton of if let statements checking for stringly-typed values inside your dictionaries whenever you access your data. Instead, each of these values should be mapped to properties on CoreData entities.

The first thing I would ask is if these outerKey dictionaries represent properties and values on my base object, or if each outerKey really represents its own model object with a relationship to the root object. If they are properties on one object, then just read the JSON and map the values to properties on your entity. If not, then you should be creating multiple entities and creating relationships like you are currently doing, but you should be creating properties for each of the values on those entities.

For the nested arrays and dictionaries, it's likely that you want to create other entities with relationships also.

Just remember that your CoreData model should reflect the objects and the way they relate to each other. It should not just mirror the structure of your JSON unless your JSON also happens to represent the correct structure.

Example

Because I don't know what your data really represents, this may not be a perfect structure for your application, but making a few assumptions about what your JSON means this is the entities that I would come up with.

- RootEntity
  - outerKey1: OuterKey1 // one to one
  - outerKey2: OuterKey2 // one to one
  - outerKey3: OuterKey3 // one to one
  - outerKey4: OuterKey4 // one to one

- OuterKey1
  - someKey: String
  - disableAutoFill: Bool
  - disableABC: Bool
  - disableXYZ: Bool
  - disableThis: Bool
  - disableThat: Bool
  - disableBla: Bool
  - disableBlaBla: Bool
  - disableBlaBlaBlaBla: Bool
  - disableBlaBlaBlaBlaBlaBla: Bool

- OuterKey2
  - someKey: [SomeKeyEntity] // one to many
  - enabled: Bool

- SomeKeyEntity
  - markPath: Bool
  - title: String
  - urlString: String

- OuterKey3
  - gatewayIP: String
  - gatewayPort: Int
  - gatewayRoutingURLs: [String] // This can be a transformable `Array`, unless you think you will need to query based on this property later
  - enabled: Bool

- OuterKey4
  - someCategories: [SomeCategoriesEntity] // one to many
  - defaultURL: String
  - enabled: Bool
  - exceptionURLs: [String] // This can be a transformable `Array`, unless you think you will need to query based on this property later
  - filterURLs: [String] // This can be a transformable `Array`, unless you think you will need to query based on this property later
  - filteringFlag: Bool

Hope that helps!

AnthonyMDev
  • 1,496
  • 1
  • 13
  • 27
  • 1
    let me tell you one good thing about the requirement, I never gonna query anything within the model, whenever I'll access this data, I need whole data, not a piece of it and second, this JSON is actually reflect the object. Thank you. – Suryakant Sharma Feb 25 '16 at 04:49
  • 2
    if you only ever use it in a block - then it's one piece of data and not many. You could just store it as a block of JSON text with `someKey` as the identifier – Russell Feb 25 '16 at 15:32
  • Okay, then, I would still use the object structure from above, but I would just make all the child entities transformable objects that conform to `NSCoding` rather than just using regular dictionaries. – AnthonyMDev Feb 26 '16 at 21:19
  • 1
    Thanks Anthony for your efforts... I have awarded you bounty points. – Suryakant Sharma Feb 29 '16 at 10:25
1

Yeah you don't want to do it the way you are doing, completely misses the point. You can either digest all of that and build a proper object model, or just save the JSON to the filesystem and then just reload and get the data you want from it when you need it. There are a variety of JSON based parsers that would allow you to do things like:

BOOL autofillDisabled = [jsonFile boolFor:@"outerkey.disableAutoFill"];

This would be using the JSON directly as your data store. If you really need to build an object model based on this data, good luck.

Hah, j/k. One way I have solved this exact problem (although never with such randomly formed json. Is after you get the root dictionary, and having some understanding of the data and how you want to use it, build a simple object cascade system to drill down into the data.

For example, start here:

@interface ParseOperation : NSObject
+ (void) updateObject:(id)object fromDictionary:(NSDictionary *)aDict;
@end

Then you will proceed to create the meat of each of your objects something like this:

@implementation SomeOperation // Which is a subclass of ParseOperation

+ (void) updateObject:(SomeObject *)object fromDictionary:(NSDictionary *)aDict
{
    object.caption = [ParseObject stringForTag:@"caption" in:aDict];
    object.status = [ParseObject stringForTag:@"status" in:aDict];
    object.event = [ParseObject stringForTag:@"event" in:aDict];
}

Then just create your driver mechanism:

    identifier = [ParseOperation stringForTag:@"SomeObject" in:dict];
    SomeObject * object = (SomeObject *)[self object:NSStringFromClass([SomeObject class]) withIdentifier:identifier];
    object.identifier = identifier;
    [SomeOperation updateObject:object fromDictionary:dict];

The utility of doing it this way is that you can easily handle the complexity of JSON like in this case:

id check = [aDict objectForKey:@"subArray"];
// This is a mapping file you create, replacing "subArray" with your object's key
NSString * objectKey = [mapping objectForKey:@"object"];
if([check isKindOfClass:[NSMutableArray class]])
{
    NSArray * array = (NSArray *)check;
    for(NSDictionary * dict in array)
    {
        NSString * identifier = [ParseOperation stringForTag:identifierKey in:dict];
        ManagedObject * object = [self object:objectKey withIdentifier:identifier];

        NSString * operation = [NSString stringWithFormat:@"%@Operation", objectKey];
        Class operationClass = NSClassFromString(operation);
        [operationClass updateObject:object fromDictionary:dict];
    }
}
EricLeaf
  • 892
  • 5
  • 12
0

I really don't know it will help you or not but for storing this kind of complex data I'll prefer XML based saving using KISS XML API (it is easy to use)and parse it when you need it.

Your xml file may look like

<RootEntity>    
     <OuterKey1>
           <someKey> String </someKey>
           <disableAutoFill> Bool</disableAutoFill>  //and so on
     </OuterKey1>
     <OuterKey2>
           <someKey> String </someKey>
           <disableAutoFill> Bool</disableAutoFill>  //and so on
     </OuterKey2>  
</RootEntity>
Asad Amodi
  • 136
  • 4
  • 13