Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serializing a multidimensional array containing references to NSManagedObjects in Core Data

I have two entities, Chain and Step. Chain has an attribute steps, which may be a multidimensional array of Step entities, for example:

[
  step,
  step,
  step,
  [
    step,
    step
  ],
  step
]

Each Step object has a content property that is a string.

If I were using a relational database, I'd simply store that array as JSON, with each step being the step_id of a particular step.

How would I achieve something similar in Core Data? I gather that I would need to make the Step class conform to the NSCoding Protocol, but what would that look like? How would I make it only store the equivalent of its id, i.e. a reference to itself, in the final value of Chain.steps?

EDIT

A comment below suggests that I include a one-to-many relationship (let's call it nextStep) between Step and other Steps. I would then use this relationship to go from one step to the next in a Chain, and thus the Chain entity would only need to contain the first Step in the sequence. The problem with this is that a Step may belong to more than one Chain, and thus it may not always have the same nextStep value.

like image 953
maxedison Avatar asked Mar 09 '14 15:03

maxedison


People also ask

Can unity serialize multidimensional arrays?

Unity cannot serialize multidimensional arrays, but that does not mean it is a lost cause, because Unity does give you the power to help it along. I have not looked into (or had the need for) serialization of staggered arrays, so this is out of scope for now.

How do you persist data in a multidimensional array?

I'm going to show the approach that I took that works for me. It's simple, and easy to implement. TL;DR When you want to persist data in a multidimensional array, wrap the stuff in a package, and put the packages in a list before serialization, and reconstruct your array from this list of packages after deserialization.

Are jagged arrays serializable by Unity?

I just made a general remark. jagged arrays are in fact serializable by Unity because it's just 'layers' of one-dimensional arrays, and those are serializable. Click to expand... Well, I'm not sure about that.

Is it possible to have a self referencing loop in JSONserialization?

Some serialization frameworks don't allow such cycles. For example, Json.NET will throw the following exception if a cycle is found. Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'Blog' with type 'MyApplication.Models.Blog'.


1 Answers

I would like to discourage you from serializing and deserializing arrays that include Core Data objects. It is possible, you could transform your arrays to hold the URIRepresentation of the object IDs of your steps, but what happens if you delete a step from the database? Your serialized arrays remain tainted with old objects.


Here is my proposal.

https://github.com/LeoNatan/ChainStepsExample

My model is set like this:

Model

Link is an abstract entity.

For ease of use, I have defined the following protocol:

@protocol Link <NSObject>

//This is to make chain traversal easy - all links are guaranteed to have "steps".
- (NSOrderedSet*)steps;

@end

And for creation, I added the following convenience factory methods:

@interface Link : NSManagedObject <Link>

+ (id<Link>)linkWithStepsArray:(NSArray*)stepsArray inContext:(NSManagedObjectContext*)context;
+ (id<Link>)linkWithStepsOrderedSet:(NSOrderedSet*)stepsOrderedSet inContext:(NSManagedObjectContext*)context;
+ (id<Link>)linkWithStep:(Step*)step inContext:(NSManagedObjectContext*)context;

@end

Now, creation of a chain and traversal are very easy.

Step* step1 = [Step newObjectInContext:context];
step1.content = @"Wake up";

Step* step2_1 = [Step newObjectInContext:context];
step2_1.content = @"Go to school";

Step* step2_2 = [Step newObjectInContext:context];
step2_2.content = @"Learn new things";

Step* step3 = [Step newObjectInContext:context];
step3.content = @"Go to sleep";

NSOrderedSet* links = [NSOrderedSet orderedSetWithObjects:[Link linkWithStep:step1 inContext:context], [Link linkWithStepsArray:@[step2_1, step2_2] inContext:context], [Link linkWithStep:step3 inContext:context], nil];
[chain setLinks:links];

And finally traversal:

for(Chain* chain in chains)
{
    NSLog(@"<chain %@>", chain.name);

    for(id<Link> link in chain.links)
    {
        NSLog(@"\t<step>");
        for(Step* step in link.steps)
        {
            NSLog(@"\t\t%@", step.content);
        }
        NSLog(@"\t</step>");
    }

    NSLog(@"</chain>\n\n");
}

This allows you a manageable object graph, which will rebuild itself automatically when a task is removed.

This is a simple example. It only solves one level deep. You could have a relationship between SingleStepLink or MultiStepLink and Link (instead of Step) to have an infinite depth, with leafs having the final relationship with Step.

like image 60
Léo Natan Avatar answered Sep 29 '22 07:09

Léo Natan