user798719
user798719

Reputation: 9869

AFNetworking: How to make AFHTTPClient handle both json and xml responses?

can one AFTHTTPClient handle both json and xml?

I have one single domain in which some services only return json, while others only return xml. How would I make a GET request and instruct AFHTTPClient to use AFJSONRequestOperation for some services and an AFXMLRequestOperation for other GET requests?

So what I would like is:

chairs.com  GET customerprofile  ---> returns XML (no option for json)

charis.com GET inventory ---> returns JSON (no option for xml)

Is this a job for multiple AFHTTPClients? Thanks

Upvotes: 2

Views: 1278

Answers (1)

Aaron Brager
Aaron Brager

Reputation: 66242

Your use of AFHTTPClient indicates you're using AFNetworking 1, but I'll answer this question for both versions, for future readers.

AFNetworking 1.x

You just need to register the appropriate AFHTTPOperation subclass. This is typically done in your subclass of initWithBaseURL::

- (instancetype) initWithBaseURL:(NSURL *)url {
    self = [super initWithBaseURL:url];

    if (self) {
        [self registerHTTPOperationClass:[AFJSONRequestOperation class]];
        [self registerHTTPOperationClass:[AFXMLRequestOperation class]];
    }
    
    return self;
}

When your app makes an outgoing request, you'll need to make sure your accept headers are set appropriately (for example, to text/json or text/xml, depending on what you expect from which endpoint you hit). Otherwise, AFNetworking won't know which operation to use for which request.

There are a few ways to easily solve this Accept header requirement. If one of your endpoints is an exception to a general rule, I might do this by overriding requestWithMethod:path:parameters::

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                      path:(NSString *)path
                                parameters:(NSDictionary *)parameters {

    request = [super requestWithMethod:method path:path parameters:parameters];

    if (/* the endpoint specified in path returns XML */) {
        [request setValue:@"text/xml" forHTTPHeaderField:@"Accept"];
    } else {
        [request setValue:@"text/json" forHTTPHeaderField:@"Accept"];
    }

}

This is a small violation of tell, don't ask; feel free to refactor as necessary.

If you don't plan on upgrading to AFNetworking 2, then you can stop reading here.

AFNetworking 2.x

Version 2.0 of AFNetworking makes this simpler and more intuitive. In 2.0, the serialization responsibility is broken out into a separate class. Instances of this class are called response serializers. When you upgrade, you'll want an AFCompoundResponseSerializer. The documentation describes it best:

AFCompoundSerializer is a subclass of AFHTTPSerializer that delegates the response serialization to the first AFHTTPSerializer object that returns YES to validateResponse:data:error:, falling back on the default behavior of AFHTTPSerializer. This is useful for supporting multiple potential types and structures of server responses with a single serializer.

For example:

AFJSONResponseSerializer *jsonSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:0];
AFXMLDocumentSerializer *xmlSerializer = [AFXMLDocumentSerializer serializerWithXMLDocumentOptions:0];

AFCompoundResponseSerializer *compoundSerializer = [AFCompoundResponseSerializer compoundSerializerWithResponseSerializers:@[jsonSerializer, xmlSerializer]];

[AFHTTPSessionManager manager].responseSerializer = compoundSerializer;

Upvotes: 7

Related Questions