POST JSON data into existing object

2020-07-02 08:31发布

问题:

I am retrieving JSON data from a URL that is formatted like so:

{"zoneresponse":
{"tasks":
 [{"datafield1":"datafor1", 
   "datafield2":"datafor2", 
   "datafield3":"datafor3",...
 }]
}}

I have no control over the structure as it is from a private API.

How do I insert data in a selected data field of an existing object?

I have tried this:

self.responseData = [NSMutableData data];

//testingURL is the api address to the specific object in tasks
NSURL *url = [NSURL URLWithString:testingURL];

NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[[[params objectForKey:@"zoneresponse"] objectForKey:@"tasks"] setValue:@"HelloWorld" forKey:@"datafield1"];
//HAVE TRIED setObject: @"" objectForKey: @"" as well

//*****PARAMS IS EMPTY WHEN PRINTED IN NSLog WHICH IS PART OF THE ISSUE - SETTING VALUE DOES NOT WORK

NSError * error = nil;

NSLog(@"Params is %@", params);

NSData *requestdata = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];

NSMutableURLRequest *request;
request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setValue:[NSString stringWithFormat:@"%d", [requestdata length]] forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:requestdata];

NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];


if(conn) {
    NSLog(@"Connection Successful, connection is: %@", conn);

} else {
    NSLog(@"Connection could not be made");
}

The connection is being made but the dictionary params is empty when printed (the setValue is not displaying) and is not entering any data into the field I select.

I have checked these links but nothing explains whether it will insert into the right field and implies it will create a new object rather than update the existing one.

How to update data on server db through json api?

How to send json data in the Http request using NSURLRequest

Delegate methods

//any time a piece of data is received we will append it to the responseData object
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [self.responseData appendData:data];

    NSError *jsonError;

    id responseDict =
    [NSJSONSerialization JSONObjectWithData:self.responseData
                                options:NSJSONReadingAllowFragments
                                  error:&jsonError];

    NSLog(@"Did Receive data %@", responseDict);
}

 //if there is some sort of error, you can print the error or put in some other handling here, possibly even try again but you will risk an infinite loop then unless you impose some sort of limit
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    // Clear the activeDownload property to allow later attempts
    self.responseData = nil;

    NSLog(@"Did NOT receive data ");

}

//connection has finished, the requestData object should contain the entirety of the response at this point
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSError *jsonError;
    id responseDict =
    [NSJSONSerialization JSONObjectWithData:self.responseData
                                options:NSJSONReadingAllowFragments
                                  error:&jsonError];
    if(responseDict)
    {
        NSLog(@"%@", responseDict);
    }
    else
    {
        NSLog(@"%@", [jsonError description]);
    }

    //clear out our response buffer for future requests
    self.responseData = nil;
}

The first method here states that data was received with "Did Receive data (null)", there is no error with connection however the final method prints the error message "JSON text did not start with array or object and option to allow fragments not set.", which is understandable because there is no data or object being sent.

How do I insert data into a selected field of an existing object?

回答1:

You do this wrong:

NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[[[params objectForKey:@"zoneresponse"] objectForKey:@"tasks"] setValue:@"HelloWorld" forKey:@"datafield1"];

Your params is empty dictionary and no object will be returned with [params objectForKey:@"zoneresponse"].

Try this:

NSMutableDictionary *params = [NSMutableDictionary new];
params[@"zoneresponse"] = @{@"tasks": @{@"datafield1": @"HelloWorld"}};

This will work but object for key @"tasks" will be immutable. To add another objects to tasks dictionary we need to make it mutable:

NSMutableDictionary *params = [NSMutableDictionary new];
NSMutableDictionary *tasks = [NSMutableDictionary new];
params[@"zoneresponse"] = @{@"tasks": tasks};
params[@"zoneresponse"][@"tasks"][@"datafield1"] = @"HelloWorld";

or

NSMutableDictionary *params = [NSMutableDictionary new];    
params[@"zoneresponse"] = @{@"tasks": [@{@"datafield1": @"HelloWorld"} mutableCopy]};

Then you can add another objects to tasks:

params[@"zoneresponse"][@"tasks"][@"datafield2"] = @"HelloWorld2";
params[@"zoneresponse"][@"tasks"][@"datafield3"] = @"HelloWorld3";

I think my answer will bring some clarity to operations with dictionaries for you.



回答2:

Architectural context of your approach to sending JSON data to a servers database:

You are using an old method (since 03') of making a HTTP POST request to get your lovely JSON data in to the servers database. It is still functional and not deprecated and ultimately an acceptable approach. Typically how this approach works is you set up and fire a NSURLRequest with a NSURLConnection, the ViewController or object that fired the request typically implements the NSURLConnection protocol so you have a callback method that receives the NSURLRequests associated response as you've done. Theres also some NSURLCaching and NSHTTPCookStorage available to avoid redundancy and speed up the whole thing.

There is a new way (since 13'):

NSURLConnections successor is NSURLSession. Since Vladimir Kravchenko's answer focusses on forming the NSDictionary to be sent as a parameter who showed that you need to use a NSMutableDictionary instead of a static NSDictionary which can't be edited after it's init'ed. I'll focus on the networking approach surrounding your question though in order to be helpful.

/*
The advantage of this code is it doesn't require implementing a
protocol or multiple callbacks.
It's self contained, uses a more modern framework, less code
and can be just thrown in to the viewDidAppear

Basically - theres less faffing about while being a little easier to understand.
*/
NSError *requestError;
// session config
NSURLSessionConfiguration *configuration = 
[NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = 
[NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
// setup request
NSURL *url = [NSURL URLWithString:testingURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:60.0];

[request addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request addValue:@"application/json" forHTTPHeaderField:@"Accept"];
[request setHTTPMethod:@"POST"];
// setup request parameters, Vladimir Kravchenko's use of literals seems fine and I've taken his code
NSDictionary *params = @{@"zoneresponse" : @{@"tasks" : [@{@"datafield1" : @"HelloWorld"} mutableCopy]}};
params[@"zoneresponse"][@"tasks"][@"datafield2"] = @"HelloWorld2";
params[@"zoneresponse"][@"tasks"][@"datafield3"] = @"HelloWorld3";
// encode parameters
NSData *postData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&requestError];
[request setHTTPBody:postData];
// fire request and handle response
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:
    ^(NSData *data, NSURLResponse *response, NSError *error) 
{
    NSError *responseError;
    // parse response
    NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:data
                                                                 options:NSJSONReadingAllowFragments
                                                                   error:&responseError];
    // handle response data
    if (responseError){
        NSLog(@"Error %@", responseError)
    }else{
        if (responseDict){
            NSLog(@"Response %@", responseDict);
        }
    }
}];

[postDataTask resume];

Further Reading

The always wonderful Mattt Thompson has written about the transition from NSURLConnection to NSURLSession here:objc.io

You can find another similar Stack Overflow Questions here:Stack Overflow

If you would like a networking library that makes these issues a little easier check out Mattt Thompsons AFNetworking which can be found here: GitHub

Further analysis on NSURLConnection versus NSURLSession can be found here:Ray Wenderlich