"Unable to find any mappings for the given content, keyPath=null" RestKit 0.2 - ios6

So I switched to using RestKit 0.2 and CoreData and I've been having a lot of trouble trying to get the mappings correct... I don't understand why. The JSON Response of my server is like this:
{
"meta":
{
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 2
},
"objects":
[{
"creation_date": "2012-10-15T20:16:47",
"description": "",
"id": 1,
"last_modified":
"2012-10-15T20:16:47",
"order": 1,
"other_names": "",
"primary_name": "Mixing",
"production_line": "/api/rest/productionlines/1/",
"resource_uri": "/api/rest/cells/1/"
},
{
"creation_date": "2012-10-15T20:16:47",
"description": "",
"id": 2,
"last_modified": "2012-10-15T20:16:47",
"order": 2, "other_names": "",
"primary_name": "Packaging",
"production_line": "/api/rest/productionlines/1/",
"resource_uri": "/api/rest/cells/2/"
}]
}
Then in XCode I have:
RKObjectManager *objectManager = [RKObjectManager sharedManager];
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
objectManager.managedObjectStore = managedObjectStore;
RKEntityMapping *cellMapping = [RKEntityMapping mappingForEntityForName:#"Cell" inManagedObjectStore:managedObjectStore];
cellMapping.primaryKeyAttribute = #"identifier";
[cellMapping addAttributeMappingsFromDictionary:#{
#"id": #"identifier",
#"primary_name": #"primaryName",
}];
RKResponseDescriptor *responseCell = [RKResponseDescriptor responseDescriptorWithMapping:cellMapping
pathPattern:#"/api/rest/cells/?format=json"
keyPath:#"objects"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptorsFromArray:#[responseCell, responseUser, responseCompany]];
[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"AppDB.sqlite"];
NSString *seedPath = [[NSBundle mainBundle] pathForResource:#"SeedDatabase" ofType:#"sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, #"Failed to add persistent store with error: %#", error);
// Create the managed object contexts
[managedObjectStore createManagedObjectContexts];
// Configure a managed object cache to ensure we do not create duplicate objects
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
My request is:
[[RKObjectManager sharedManager] getObjectsAtPath:#"/api/rest/cells/?format=json" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
RKLogInfo(#"Load complete: Table should refresh...");
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:#"LastUpdatedAt"];
[[NSUserDefaults standardUserDefaults] synchronize];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(#"Load failed with error: %#", error);
}];
And I always get the following error:
**Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "Unable to find any mappings for the given content" UserInfo=0x1102d500 {DetailedErrors=(), NSLocalizedDescription=Unable to find any mappings for the given content, keyPath=null}**
Thanks a lot!
UPDATE: I have added cellMapping.forceCollectionMapping = YES;
but still no luck :(!
UPDATE #2: Following Blake's advice, I tried to change the path and it worked! I did /api/rest/cells/ instead of /api/rest/cells/?format=json and my server returned me everything and the mapping was successful!
Now the only problem I get is the following error:
2012-11-21 14:48:49.414 App[3125:617] W restkit.object_mapping:RKMapperOperation.m:176 Collection mapping forced but mappable objects is of type '__NSCFArray' rather than NSDictionary

It sounds like the response descriptor is failing to match for the URL requested. Two ideas:
Try removing the path pattern entirely (pass in nil) and just using a key-path based match on 'cells'
Try using a path pattern of '/api/rest/cells'
The next thing I would try is using the debugger to step through the matching. Within RKObjectManager a list of candidate response mapping is built by the RKFilteredArrayOfResponseDescriptorsMatchingPath function. If your expected response mapping is not being returned in there, then the request path to path pattern is failing to evaluate.
If things look good there, the next place a mismatch could occur is in RKResponseMapperOperation within the buildResponseMappingsDictionary method. This method evaluates the response against each response descriptor. If the response is failing to match against your response descriptor, then you'll get unexpected results here.
The final place to check is within RKResponseMapperOperation. This takes the mappings from matched descriptors and applies them. The RKMapperOperation main method should contain the deserialized response you expect and the appropriate mappings on the mappingsDictionary property.

Even when the setup seems to be right, the mapping is wrong. The information your mapping is calling is nested inside the given objects, so you've to tell the mapping to look up the values inside of the objects.
RKEntityMapping *cellMapping = [RKEntityMapping mappingForEntityForName:#"Cell" inManagedObjectStore:managedObjectStore];
cellMapping.primaryKeyAttribute = #"identifier";
[cellMapping mapKeyOfNestedDictionaryToAttribute:#"objects"];
[cellMapping mapFromKeyPath:#".id" toAttribute:"identifier"];
[cellMapping mapFromKeyPath:#"primary_name" toAttribute:"primaryName"];
For more information check the RKObjectMapping Reference.

Related

Getting values from json array using an array of object and keys in Python

I'm a Python newbie and I'm trying to write a script to extract json keys by passing the keys dinamically, reading them from a csv.
First of all this is my first post and I'm sorry if my questions are banals and if the code is incomplete but it's just a pseudo code to understand the problem (I hope not to complicate it...)
The following partial code retrieves the values from three key (group, user and id or username) but I'd like to load the objects and key from a csv to make them dinamicals.
Input json
{
"fullname": "The Full Name",
"group": {
"user": {
"id": 1,
"username": "John Doe"
},
"location": {
"x": "1234567",
"y": "9876543"
}
},
"color": {
"code": "ffffff",
"type" : "plastic"
}
}
Python code...
...
url = urlopen(jsonFile)
data = json.loads(url.read())
id = (data["group"]["user"]["id"])
username = (data["group"]["user"]["username"])
...
File.csv loaded into an array. Each line contains one or more keys.
fullname;
group,user,id;
group,user,username;
group,location,x;
group,location,y;
color,code;
The questions are: can I use a variable containing the object or key to be extract?
And how can I specify how many keys there are in the keys array to put them into the data([ ][ ]...) using only one line?
Something like this pseudo code:
...
url = urlopen(jsonFile)
data = json.loads(url.read())
...
keys = line.split(',')
...
# using keys[] to identify the objects and keys
value = (data[keys[0]][keys[1]][keys[2]])
...
But the line value = (data[keys[0]][keys[1]][keys[2]]) should have the exact number of the keys per line read from the csv.
Or I must to make some "if" lines like these?:
...
if len(keys) == 3:
value = (data[keys[0]][keys[1]][keys[2]])
if len(keys) == 2:
value = (data[keys[0]][keys[1]])
...
Many thanks!
I'm not sure I completely understand your question, but I would suggest you to try and play with pandas. It might be as easy as this:
import pandas as pd
df = pd.read_json(<yourJsonFile>, orient='columns')
name = df.fullname[0]
group_user = df.group.user
group_location = df.group.location
color_type = df.color.type
color_code = df.color.code
(Where group_user and group_location will be python dictionaries).

How to update PostgreSQL array of jsonb

I have a table like:
id: integer,
... other stuff...,
comments: array of jsonb
where the comments column has the following structure:
[{
"uid": "comment_1",
"message": "level 1 - comment 1",
"comments": [{
"uid": "subcomment_1",
"message": "level 2 - comment 1",
"comments": []
}, {
"uid": "subcomment_2",
"message": "level 1 - comment 2",
"comments": []
}]
},
{
"uid": "P7D1hbRq4",
"message": "level 1 - comment 2",
"comments": []
}
]
I need to update a particular field, for example:comments[1](with uid = comment_1) -> comments[2] (with uid = subcomment_2) -> message = 'comment edited'.
I'm brand new to postgresql and I can't figure it out how to do this, not even close. I manage to merge objects and change message for level 1 with:
UPDATE tasks
set comments[1] = comments[1]::jsonb || $$
{
"message": "something",
}$$::jsonb
where id = 20;
but that's as far as I could go.
Any hints towards the right direction?
LE:
I got this far:
UPDATE tasks
set comments[1] = jsonb_set(comments[1], '{comments,1, message}', '"test_new"')
where id = 20;
Sure, I can get this path from javascript but it's that a best practice? Not feeling comfortable using indexes from javascript arrays.
Should I try to write a sql function to get the array and use the 'uid' as key? Any other simpler way to search/select using the 'uid' ?
LLE
I can't get it to work using suggestion at:this question (which I read and tried)
Code bellow returns nothing:
-- get index for level 2
select pos as elem_index
from tasks,
jsonb_array_elements(comments[0]->'comments') with ordinality arr(elem, pos)
where tasks.id = 20 and
elem ->>'uid'='subcomment_1';
and I need it for several levels so it's not quite a duplicate.
First, you cannot update a part of a column (an element of an array) but only a column as a whole.
Next, you should understand what the path (the second argument of the jsonb_set() function) means.
Last, the third argument of the function is a valid json, so a simple text value must be enclosed in both single and double quotes.
update tasks
set comments = jsonb_set(comments, '{0, comments, 1, message}', '"comment edited"')
where id = 1;
Path:
0 - the first element of the outer array (elements are indexed from
0)
comments - an object with key comments
1 - the second element of
the comments array
message - an object message in the above
element.
See Db<>fiddle.

What is the meaning of []json.Rawmessage

What is the meaning of []json.Rawmessage. Its within this structure here:
type Request struct {
Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"`
Params []json.RawMessage `json:"params"`
ID interface{} `json:"id"`
}
I know its a slice of type json. I do not understand what the .RawMessage is referring to. I tried looking it up in both golang tour and my golang book. Also ultimately I know Params is type []json.Rawmessage being bundled into another type called Request
Futhermore:
What is going on with these segments json:"jsonrpc". Is a string literal somehow being attached to var? Again this is not in golang tour or my golang book. Thanks for your time.
[] is defining a slice
json is the package import name (from your import statement above)
RawMessage is the type within the package. In this case for a []byte type.
json:"params" is a field tag. The json package reads it with reflection and determines what name to use for json.
Most of the time time you need to look in to the package doc rather than some book and the online tour.
The json:"jsonrpc" is a struct tag.
For this specific case the json.Marshal function will read that struct tag and return the JSON field name with the given value. For more about "encoding/json" struct tags usage: https://golang.org/pkg/encoding/json/#Marshal
For RawMessage, you can read more about it here https://golang.org/pkg/encoding/json/#RawMessage
type RawMessage []byte
Normally I use it when having a "generic" kind of JSON object that don't need to be process right the way (maybe I just send them to another service, or I will unmarshall it later depending on conditions). For example (I will use your Request struct):
jsonString := `[
{
"id": 123,
"method": "getSomething",
"params": [{"A": 1, "B": 2}]
}
{
"id": 123,
"method": "getSomethingElse",
"params": [{"C": 1, "D": 2}]
}
]`
With this processing code:
var requests []Request
json.Unmarshal([]byte(jsonString), &requests)
// if no error, you will have 2 items in requests
// requests[0].Params[0] is equal to []byte(`{"A": 1, "B": 2}`)
// requests[1].Params[0] is equal to []byte(`{"C": 1, "D": 2}`)
for _, req := range requests {
if req.Method == "getSomething" {
justProxyThisRequestToAnotherService(req)
} else if req.Method == "getSomethingElse" {
var params []map[string]int
json.Unmarshal(req.Params, &params)
// then do something with params
}
}

How to highlight continent

I have to show statistics on a continent level (Europe, Asia, etc.).
How can I pass the data? I can find only samples to pass the data on a country level like:
var sample_data0 = { "de": "10000", "at": "15000", "pl": "5000" };
I would like something like:
var sample_continent-data = { "Europe": "10000", "Asia": "15000", "northamerica": "5000" };
I know this is old but here's an answer for you....
Replace the code in your jquery.vmap.world.js file with this file (make a back up of your original):
http://www.filedropper.com/jqueryvmapworld
(There's too much data to paste it here - hence the link to a file.)
This will give you a map with continents rather than countries.
Then replace the code in jquery.vmap.sampledata.js with:
var sample_data = { "AF": "152.23", "NA": "11.58", "OC": "158.97", "AS": "85.81", "EU": "1.1", "SA": "351.02" };
That should just work now (assuming you had the basic maps working) - comment if anything has gone wrong, or the linked file has disappeared.

Import .csv data into a array

I'm using Objective-C the last years.
Now I'm trying the Xcode 6 beta 4 with swift.
I want to import a .csv form my webserver to an array. My old code in Objective-C was:
NSString *stringURL = #"https:// [URL] /versionen/versionen.csv";
NSURL *url = [NSURL URLWithString:stringURL];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if ( urlData )
{
NSString *csvResponseString = [[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding];
NSArray *MZVersionDatenZeilen = [csvResponseString componentsSeparatedByString:#"\n"];
NSEnumerator *MZVersionEnumerator = [MZVersionDatenZeilen objectEnumerator];
NSMutableArray *MZVersionDatenArray = [NSMutableArray arrayWithCapacity:[MZVersionDatenZeilen count]];
NSString *MZVersionZeile;
while (MZVersionZeile = [MZVersionEnumerator nextObject])
{
[MZVersionDatenArray addObject:[MZVersionZeile componentsSeparatedByString:#";"]];
}
}
How could I do this in Swift?
Is there a best practices - recommendation?
Every answer requires you to install/download a 3rd party utility, but that may not be optimal if the file you're working with is extremely simple or you work at an company that restricts 3rd party code.
So I figured I would just post incredibly basic, routine CSV file handling code that people can use as a base and modify as needed for simple CSV handling.
do {
let file = try String(contentsOf: fileUrl)
let rows = file.components(separatedBy: .newlines)
for row in rows {
let fields = row.replacingOccurrences(of: "\"", with: "").components(separatedBy: ",")
print(fields)
}
} catch {
print(error)
}
This will handle any CSV files that you know won't have quotes and commas as part of the content of a field, which in my case are most CSV files. Anything more complex than this, I recommend using one of the libraries posted in the other answers.
I just hope this saves someone a bit of typing.
There are multiple swift libraries available:
CSVImporter, which is an asynchronous parser suitable for working with large csv files.
let path = "path/to/your/CSV/file"
let importer = CSVImporter<[String]>(path: path)
importer.startImportingRecords { $0 }.onFinish { importedRecords in
for record in importedRecords {
// record is of type [String] and contains all data in a line
}
}
SwiftCSV, which is a simple CSV parsing library for OSX and iOS.
let csvURL = NSURL(string: "users.csv")!
var error: NSErrorPointer = nil
let csv = CSV(contentsOfURL: csvURL, error: error)
// Rows
let rows = csv.rows
let headers = csv.headers //=> ["id", "name", "age"]
let alice = csv.rows[0] //=> ["id": "1", "name": "Alice", "age": "18"]
let bob = csv.rows[1] //=> ["id": "2", "name": "Bob", "age": "19"]
// Columns
let columns = csv.columns
let names = csv.columns["name"] //=> ["Alice", "Bob", "Charlie"]
let ages = csv.columns["age"] //=> ["18", "19", "20"]
and CSwiftV, which is a csv parser conforming to rfc4180 spec, but according to author it is all in memory so not suitable for large files.
let inputString = "Year,Make,Model,Description,Price\r\n1997,Ford,E350,descrition,3000.00\r\n1999,Chevy,Venture,another description,4900.00\r\n"
let csv = CSwiftV(String: inputString)
let headers = csv.headers // ["Year","Make","Model","Description","Price"]
let rows = csv.rows
// [
// ["1997","Ford","E350","descrition","3000.00"],
// ["1999","Chevy","Venture","another description","4900.00"]
// ]
Don't use SwiftCSV as per the currently most upvoted answer.
SwiftCSV does not handle double quotes as of right now so if any data in your CSV file has line breaks or commas in it, SwiftCSV won't work. And you might waste valuable development time finding out exactly why it doesn't work ... to save you that time, just use another library.
The CSwiftV library worked very well for me:
https://github.com/Daniel1of1/CSwiftV
It handles quoted text, newlines, commas, worked like a charm on my data. It also has unit tests and conforms to the rfc4180 standard.
I recommend using CSVImporter – it takes care of things like quoted text (following RFC 4180) for you and even handles very large files without problems.
Compared to other solutions it works both asynchronously (prevents delays) and reads your CSV file line by line instead of loading the entire String into memory (prevents memory issues). On top of that it is easy to use and provides beautiful callbacks for indicating failure, progress, completion and even data mapping if you desire to.
The simplest way of using it is this (gives you each line as an array of Strings):
let path = "path/to/your/CSV/file"
let importer = CSVImporter<[String]>(path: path)
importer.startImportingRecords { $0 }.onFinish { importedRecords in
for record in importedRecords {
// record is of type [String] and contains all data in a line
}
}
Take advantage of more sophisticated features like data mapping and progress callbacks:
let path = "path/to/Hogwarts/students"
let importer = CSVImporter<Student>(path: path)
importer.startImportingRecords { recordValues -> Student in
// define your data mapping here
return Student(firstName: recordValues[0], lastName: recordValues[1])
}.onProgress { importedDataLinesCount in
// use this to indicate progress
print("\(importedDataLinesCount) lines were already imported.")
}.onFinish { importedRecords in
for student in importedRecords {
// now importedRecords is an array of Student objects
}
}

Resources