Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RLMObject with array of strings #1028

Closed
Bruno-Furtado opened this issue Oct 16, 2014 · 11 comments
Closed

RLMObject with array of strings #1028

Bruno-Furtado opened this issue Oct 16, 2014 · 11 comments

Comments

@Bruno-Furtado
Copy link

I have the following JSON and I need to convert it to object:

{
"title" : "Title",
"description" : "Description",
"tags" : ["First", "Second"]
}

I tried using the following class to serialize:

@interface Object : RLMObject
@property NSString *title;
@property NSString *description;
@property RLMArray<Tag> *tags;
@end

@interface Tag : RLMObject
@property NSString *value;
@end
RLM_ARRAY_TYPE(TGTag)

The following error message appears:

Terminating app due to uncaught exception 'RLMException', reason: 'Invalid value 'nil' for property 'value''

How could I serialize an array of pure strings?
Thanks and sorry for the silly question but have not found anything about it.

@GreatApe
Copy link

Hello Bruno-Fortado, thank you for trying Realm! It looks like you are trying to set a string property to nil, which is not possible at the moment. Could you share the code that produces the error?

(Looking at your model, is this exactly what it looks like? It looks like it shouldn't work, because the Tag interface should come before that for Object, and the RLM_ARRAY_TYPE macro has TGTag as argument.)

@Bruno-Furtado
Copy link
Author

@GreatApe, sorry.
Exemplified in the wrong way.

Is another example:

JSON

{
"title" : "Title",
"rating" : 90,
"tags" : ["Tag1", "Tag2"]
}

NSDictionary

{
    rating = 90;
    tags =     (
        "Tag1",
        "Tag2"
    );
    title = "Title";
}

Models

#import "RLMObject.h"
#import "Tag.h"

@interface Recipe : RLMObject
@property NSString *title;
@property float rating;
@property RLMArray<Tag> *tags;
@end
#import "RLMObject.h"

@interface Tag : RLMObject
@property NSString *value;
@end
RLM_ARRAY_TYPE(Tag)

NSDictionary to RLMObject

AFHTTPRequestOperationManager *operationManager = [AFHTTPRequestOperationManager instance];
[operationManager GET:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
     // responseObject is NSDictionary
     Recipe *recipe = [[Recipe alloc] initWithObject:responseObject];        
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
     completion(nil);
}];

Error

The error occurs when the object starts from the dictionary:

2014-10-16 08:41:50.445 App[77768:8853055] *** Terminating app due to uncaught exception 'RLMException', reason: 'Invalid value 'nil' for property 'value''

@GreatApe
Copy link

OK, thank you for clarifying. The issue is most likely that you are setting the tags property to the value @[@"tag1", @"tag2"] which is an NSArray, and not an RLMArray with object type Tag. You would need to manually parse it. One way would be to initWithObject with a dictionary where the tags property is an empty NSArray, and then manually populate that array with Tag objects.

@Bruno-Furtado
Copy link
Author

@GreatApe,
Thank you so much!

Just a doubt, this array will be saved to the database normally?

@GreatApe
Copy link

Well, it depends on what you mean by 'normally', but probably the answer is yes!

Also, another way to do it would be to supply an NSArray of Tag objects, that would also work. So you would first create the Tag objects using the strings in that array, then put the Tag:s in an array and replace the string array in the dictionary with that. Then you can use initWithObject.

The thing is that if you give it an array of NSStrings when it expects an array of Tag:s is that it will not create those Tag:s from the strings. It is not that automatic. In this case it is pretty straightforward since Tag:s only have one property, which is an NSString, but generally speaking the RLMObject subclass that is contained in the array property could be arbitrarily complex.

This project would probably be useful for you if you want to parse anything more complex:
https://github.com/matthewcheok/Realm-JSON

@alazier
Copy link
Contributor

alazier commented Oct 16, 2014

@GreatApe is incorrect here. You can pass in an array of object literals which represent Tag objects and they will be created automatically. For example:

Recipe *recipe = [[Recipe alloc] initWithObject:@[@"title", @2, @[@[@"tag1"], @[@"tag2"]]]];

would create the recipe and its two tag automatically. The key is that each tag (and any other object) is represented by an array (or dictionary) of all its properties. Even in the case of Tag where there is one property it needs to be an array and not just the string which is the value of the sole property.

@alazier alazier closed this as completed Oct 16, 2014
@GreatApe
Copy link

Sorry about that. In other words initWithObject is recursive but since it requires you to put the properties of a nested object in an array the dictionary you have will still need to be parsed, but at least you will not have to create the nested objects manually.

@ipeisong
Copy link

doesn't realm support sth like RLMArray ?

@GreatApe
Copy link

Sure, realm supports RLMArray, but an RLMArray can only contain instances of (subclasses of) RLMObjects, so you can't make an RLMArray of strings directly. In this case there was an RLMArray of Tag objects, where each Tag object had a single property, an NSString. So in order to init a Tag object you would have to do this:

Tag *aNewTagObject = [[Tag alloc] initWithObject:@["someTag"]];

or this:

Tag *aNewTagObject = [[Tag alloc] initWithObject:@{@"value" : "someTag"}];

So even if an RLMObject subclass only has one property, you have to put that property in an array or dictionary when you initialise it. Consequently, if you want to initialise an instance of the Recipe class, which contains a property that was an array of Tag objects, you have to put the value of each tag property in an array or dictionary, like in Ari's example:

{
    "title" : "Title",
    "description" : "Description",
    "tags" : [["First"], ["Second"]]
}

which is equivalent to:

{
    "title" : "Title",
    "description" : "Description",
    "tags" : [aTagInstanceWithValuePropertyEqualToFirst, aTagInstanceWithValuePropertyEqualToSecond]
}

but the dictionary that Bruno used had the tag strings directly in the tags array like this:

{
    "title" : "Title",
    "description" : "Description",
    "tags" : ["First", "Second"]
}

and then when initWithObject reaches the tags property it expects an array of Tag instances (or something that a recursive call to initWithObject can turn into that) and instead just finds an array of strings.

@ipeisong
Copy link

if a RLMObject has only one property, the initWithObject will find it even without having a default property name? In this case the property name is 'value', but if I name it as 'stringValue' it will work exactly the same, right?

@GreatApe
Copy link

Yes, and that actually works even if there are multiple properties, but then you need to make sure they are in the correct order. That is why in Ari's example he could initialise the Recipe object simply like this:

@[@"title", @2, @[@[@"tag1"], @[@"tag2"]]]

instead of this:

@{@"title" : @"title", @"rating" : @2, @"tags : "@[@[@"tag1"], @[@"tag2"]]}

which would also work.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 18, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants