I need to be able to read some JSON that I no control over. Basically the JSON looks like this:
[ [{"a":1,"b":2}], [{"a":1,"b":2}], [{"a":1,"b":2}] ]
I'm trying to parse it with RestKit and I just couldn't figure out how to handle the first two levels of the object hierarchy. The items in question are more complicated but they are not the issue here. The issue is how should I skip the second array that empirically would seem to have only one item every time.
In short, I'd like to flatten this and get single array instead of an array of arrays.
I've tried to create a mapping for NSArraybut from there I have no idea how to map the items in this array. The inner array has no name and I couldn't figure out how to reference it in the mappings.
Any working solution is greatly appreciated.
Update
The issue here is how should I create the JSON mappings and not how to read multi-dimensional arrays. I've tried the following but I don't know if the mapping for NSArray is ok. The following mapping gives an example, but it doesn't work:
secondMapping = [RKObjectMapping mappingForClass:[MyClass class]];
[secondMapping addAttributeMappingsFromDictionary:#{
#"a": #"a",
#"b": #"b"
}];
firstMapping = [RKObjectMapping mappingForClass:[NSArray class]];
[firstMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:nil
toKeyPath:nil
withMapping:secondMapping]];
Can't you just copy over the single element of the inner arrays to another array?
NSMutableArray *newArr = [NSMutableArray new];
for (NSArray *innerArr in jsonObject)
[newArr addObject:innerArr[0]];
You don't say what you're trying to map to, it will be a little difficult to map the base dictionary into a custom object. If you create an object with an array property:
#property (strong, nonatomic) NSArray *items;
Then you can map an array of these objects where the items array will contain NSDictionary instances. This would use a nil keypath to map to the items key.
The best way to make this work without giving up on RestKit was to add a custom JSON serializer. I only needed the secondMapping and mappings from there on required to match the object hierarchy.
[RKMIMETypeSerialization registerClass:[MySerialization class] forMIMEType:#"application/json"];
And here is my serializer:
#implementation MySerialization
+ (id)objectFromData:(NSData *)data error:(NSError **)error
{
id object = [NSJSONSerialization JSONObjectWithData:data options:0 error:error];
// Fix the following weirdness in JSON response:
// [ [{route_1}], ..., [{route_n}] ]
if ([object isKindOfClass:[NSArray class]]
&& [[object lastObject] isKindOfClass:[NSArray class]])
{
object = [object valueForKeyPath:#"#unionOfArrays.#self"];
}
return object;
}
+ (NSData *)dataFromObject:(id)object error:(NSError **)error
{
return [NSJSONSerialization dataWithJSONObject:object options:0 error:error];
}
#end
Other alternative is block in RKResponseMapperOperation mentioned here
- (void)setWillMapDeserializedResponseBlock:(id ( ^ ) ( id deserializedResponseBody ))block
I chose the custom serializer as it was simpler, but this is definitely more scalable way.
From my experience with RestKit, this will only work for ONE mapping. Use #"" as the root keypath and you should be able to get it to work. However what you probably need to do if you intend to use RestKit for your entire application is to modify the response data to include the root element name.
Related
I'm receiving a JSON payload of data from a MVC API in my iOS application. NSJSONSerialization then serializes this into an object. This object contains some properties and also a list of data objects. The data objects are of type NSDictionary. I already have the class structure of these objects in ObjC (Im using odata so i want to convert the objects to their OdataObject equivalent).
So I'd like to know how I can cast/convert these NSDictionary objects to their corresponding OdataObject class (or any object really)?
You can't cast an NSDictionary instance to be an OdataObject, you either need to explicitly convert the instance or create the appropriate instance when you deserialise the JSON.
You could look at using setValuesForKeysWithDictionary: to push your dictionary contents into another instance using KVC. Whether this will work in this case depends on the OdataObject definition (from github? Not convinced) and the dictionary contents...
Write a class category for NSDictionary that allows the conversion to the OdataObject class? I'm sorry, I don't completely understand what you're asking but if you need to be able to convert NSDictionary to a custom object, then I recommend Class Categories:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html
Yes, you can not cast your NSDictionary instance to your custom model object. For that you need to write code of conversion.
1) Create a class which inherits NSObject with required properties.
2) Synthesize all the properties
3) Write one private keyMapping method which returns the dictionary with keys you want in your model object as
-(NSDictionary *)keyMapping {
return [[NSDictionary alloc] initWithObjectsAndKeys:
#"key1", #"key1",
#"key2", #"key2",
#"key3", #"key3",
#"key4", #"key4",
#"key5", #"key5",
nil];
}
4) Write class method which takes NSDictionary instance, as a parameter and returns instance of the same model class with filled values from NSDictionary as (Pass your dictionary to this method)
+(ModelClass *)getModelClassObjectFromDictionary:(NSDictionary *)dictionary {
ModelClass *obj = [[ModelClass alloc] init];
NSDictionary *mapping = [obj jsonMapping];
for (NSString *attribute in [mapping allKeys]){
NSString *classProperty = [mapping objectForKey:attribute];
NSString *attributeValue = [dictionary objectForKey:attribute];
if (attributeValue!=nil&&!([attributeValue isKindOfClass:[NSNull class]])) {
[obj setValue:attributeValue forKeyPath:classProperty];
}
}
return obj;
}
Thats it. Hope this helps you.
I have a json structure like this
I am trying to check for the "type" in each nested widgets array.So if it is of a certain type then i am trying to extract properties like fade steps,width, height etc.
Is there a way of doing it using the for loop?
Right now I am doing like this:
for (NSString *widgetArray in widgets)
{
NSLog(#"%#", widgetArray);
}
Its printing the contents, How do I extract corresponding values?
for(NSDictionary *asset in [assets allValues])
{
NSString *type = asset[#"type"];
if ([type isEqualToString:#"gradient"])
{
[gradients addObject:asset];
}
This is the pseudo code provided by one of the members which helped me access the contents of the dictionary, I am not able to apply a similar logic to the nested array structure.
Since the problem is recursive search, you have to use stack. Here are the steps:
Create NSMutableArray that will serve as the stack. Now it is empty.
Take the root widgets array and put all its objects onto the stack (-addObjectsFromArray:).
Take out the first widget (NSDictionary) in stack. Be sure to take it out of the stack (-removeObjectAtIndex:) after storing it in variable.
Do your type comparision and other stuff with that variable.
Take its subwidgets and put all of them onto the stack just like in step 2.
If the stack has something in it, repeat from step 3. Otherwise you processed all widgets.
Pseudo code:
// 1
NSMutableArray *stack = [NSMutableArray array];
// 2
[stack addObjectsFromArray:rootWidgets];
while (stack.count > 0) {
// 3
NSDictionary *widgetDict = [stack objectAtIndex:0];
[stack removeObjectAtIndex:0];
// 4
if ([[widgetDict objectForKey:#"type"] isEqualToString:...]) // Do whatever...
// 5
[stack addObjectsFromArray:[widgetDict objectForKey:#"widgets"]];
}
// 6
// You are done...
To check the type of each element, use the NSObject isKindOfClass: method.
So, you might have something like:
if ([obj isKindOfClass:[NSDictionary class]]) { ... }
Or
if ([obj isKindOfClass:[NSArray class]]) { ... }
Like #H2CO3 says, you use objectAtIndex: to access array elements and objectForKey: to access dictionary elements.
EDIT: I just realized that by "of a certain type" the OP meant the value of the "type" field, rather than the objective-c class of an entry. Still, it is often useful when reading JSON data to determine whether an entry is an array or dictionary, so I will leave my answer here.
In the latest objective C you can use subscripting like C arrays. For example,
NSLog(#"%#", widgetArray[i]);
For dictionaries you can extract via key:
NSString* id = myDict[#"id"];
I have a sudzc service class generated from a WSDL that accepts an ArrayOfInt and ArrayOfString objects as parameters. The service method signature is this:
- (SoapRequest*) Search: (id <SoapDelegate>) handler filters: (NSMutableArray*) displayedAttributes: (NSMutableArray*) displayedAttributes;
My question is, how do I pass values into the parameters that expect NSMutableArrays?
In the above method signature, the "displayedAttributes" parameter is expecting an ArrayOfInt object (which should be populated with several integers in an int tag, e.g., <int>1</int><int>2</int><int>3</int> etc).
However none of these things which I've tried have worked:
Directly passing an NSArray/NSMutableArray of (int) objects
Directly passing an NSArray/NSMutableArray of NSNumber objects
Passing an array of strings containing #"1", #"2", #"3" etc
Passing an array of strings that already contain #"<int>1</int>", #"<int>2</int>", etc
Constructing a CXMLDocument out of a string based on the integers
I'm sure this is somehow explained in the accompanying documentation in the download -- it's just not clear to me at the moment.
#Jon Limjap: Lucky you are!!! it asks you for a type which you have dealt before, I have custom class type that SudzC generated for me (!)... It initializes only when passed CXMLNode, (which need CXMLDocument / CXMLElement).. I have no idea how to deal with such type...
an instance is: filter is a class, I have a class of the filter, but there is no way to initialize it, (except alloc-init and then setting its properties, but its properties are another such custom type.. !!!!)...If you know any "trick" to tell/configure sudzc to allow us to pass objects or fetch objects of cocoa type, do tell me....
I had similar situation of passing array of objects to SOAP request. I managed to get it working by doing following changes.
SOAP array case in not added in
+(NSString *) serialize: (id) object()
{
//look if it is array not implemented
}
so I managed to change in the following method
+ (NSString*) serialize: (id) object withName: (NSString*) nodeName {
if([object respondsToSelector:#selector(serialize:)]) {
if([object isKindOfClass:[SoapArray class]])
return [object serialize:object];
return [object serialize: nodeName];
}
NSString *temp =[NSString stringWithFormat:#"<%#>%#</%#>", nodeName, [Soap serialize: object], nodeName];
NSLog(#"serialise = %#", temp);
return temp;
}
at the time SOAP request,
NSMutableArray arr = [[MYTable_STUB_ARR alloc] init]
MYTABLE_OBJ *obj = [[MYTABLE_OBJ alloc] init];
[arr addObject:obj];
[obj release];
Pass element arr object your SOAP Request ??
This is a bit old, but I hope it will help someone. I implemented the serialize: method on SoapArray this way:
- (NSMutableString *)serialize:(NSString *)name
{
NSMutableString *str = [NSMutableString string];
//[str appendFormat:#"<%#>", name];
for (id content in self)
{
//[str appendString:[Soap serialize:content]];
[str appendString:[Soap serialize:content withName:name]];
}
//[str appendFormat:#"</%#>", name];
return str;
}
As you can see, there are some commented lines. If you uncomment them and comment the currently used one inside the for, you will get a tag named name which will contain objects tagged with the content class name.
I have a NSDictionary with a NSString and NSArray
I have to save only the NSArray in a variable without knowing the key.
Is this possible?
If I'm understanding you correctly, you have a dictionary that contains both an NSString and an NSArray, and you want to extract just the NSArray, without knowing what the key is.
One way to do that is to look through the dictionary with fast enumeration:
NSString *key;
for(key in someDictionary){
id someObject = [someDictionary objectForKey: key];
}
and then look at the objects to see which one is an NSArray:
if ([someObject isKindOfClass:[NSArray class]]) {
// do something with the array
}
(obligatory warning: explicitly checking an object's class is often a sign of a flawed design. In most cases, you should be checking for behavior (-respondsToSelector), not class identity)
I have NSMutableArray with different objects in it of different classes. Now I want to get the class name, related stuff and also check if the respective object is NSString or not. How should I go about it?
I was trying something like the following. It wasn't working of course.
for(NSString *string in array){
NSLog(#"Name of the class : %#", [NSString stringWithCString:class_getName(Class id)];
If you're on Mac OS X, you can use [object className], it returns an NSString
for(id obj in array) NSLog(#"Name of the class: %#", [obj className]);
To check if it's a NSString, you should use something like this:
for(id obj in array) {
if ([obj isKindofClass:[NSString class]]) {
// do something
}
}
for(id object in array){
NSLog(#"Name of the class: %#", [object className]);
NSLog(#"Object is a string: %d", [object isKindOfClass:[NSString class]]);
}
Take a look at the NSObject class and protocol for other interesting methods.
I have NSMutableArray with different objects in it of different classes. Now I want to get the class name & related stuff & also check if the respective object is NSString or not.
Hold up. Why do have an array of different typed objects in the first place? Could you redo your design to avoid getting into that situation?
As others have said, -isKindOfClass: works. One downside is it generally leads to brittle code. Here your loop needs to know about all the classes that could be in the array. Sometimes this is the best you can do though.
Designs that use -respondsToSelector: tend to be a little more robust. Here your loop would need to know about the behaviors it depends on of classes in the array.