Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve anonymous type from the web in C#

Tags:

json

c#

xml

I'm trying to retrieve some data from the web. The data is served either as JSON object or XML: in both cases I'd like not to build a model based on the structure of this XML/JSON but to just retrieve the data I need.

HttpResponseMessage response = await client.PostAsync(
"http://www.someAPI.com/api.xml",
requestContent);

response.EnsureSuccessStatusCode();

HttpContent content = response.Content;

If I have to build a model based on the data structure I'll receive back I'll do it: I just want to know if there is any alternative. Can I parse content as an anonymous type and, say, retrieve the data as arbitrary fields or properties or array indexes?

Let's say: response.Countries[5].CountryId. Is it possible in any of these 2 types (JSON and XML)? How can I do it?

like image 997
Saturnix Avatar asked Aug 01 '13 08:08

Saturnix


People also ask

What is an anonymous type in C?

What Are Anonymous Types in C#? Anonymous types are class-level reference types that don't have a name. They allow us to instantiate an object without explicitly defining a type. They contain one or more read-only properties. The compiler determines the type of the properties based on the assigned values.

Do anonymous types work with Linq?

You are allowed to use an anonymous type in LINQ. In LINQ, select clause generates anonymous type so that in a query you can include properties that are not defined in the class.

How do you define anonymous type?

Essentially an anonymous type is a reference type and can be defined using the var keyword. You can have one or more properties in an anonymous type but all of them are read-only. In contrast to a C# class, an anonymous type cannot have a field or a method — it can only have properties.


2 Answers

EDIT #2:

I've added a note below about using the excellent Json.NET library to deserialize to a dynamic object.


EDIT #1:

Thanks to Hoghweed's answer, my answer below is now more complete. Specifically, we need to cast the HttpContent we get from HttpResponseMessage.Content to ExpandoObject in order for the dynamic-ness to work as expected:

dynamic content = response.Content.ReadAsAsync<ExpandoObject>().Result;
var myPropertyValue = content.MyProperty;

To get the ReadAsync<T>() extension method though, you'd need to use NuGet to download and install System.Net.Http.Formatting.dll from the Microsoft.AspNet.WebApi.Client package (here's the "old" Nuget page, which mentions that it is now included in the above package).


Original Answer:

So, you don't want to have to create a POCO and have to manage its properties as the XML/JSON structure you get back changes. dynamic seems perfect for your use case:

HttpResponseMessage response = await client.PostAsync(
"http://www.someAPI.com/api.xml", requestContent);

response.EnsureSuccessStatusCode();

dynamic content = response.Content.ReadAsAsync<ExpandoObject>().Result; // Notice the use of the dynamic keyword
var myPropertyValue = content.MyProperty; // Compiles just fine, retrieves the value of this at runtime (as long as it exists, of course)

Specifically regarding XML: you could try Anoop Madhusudanan's ElasticObject which might be very helpful when converting between dynamic and XML, and back.

Specifically regarding JSON: you could use Json.NET do something like this:

dynamic content = JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result);
var myPropertyValue = content.MyProperty;

The up-side is that you won't take a dependency on the Microsoft.AspNet.WebApi.Client package (which, as of v4.0.30506.0, depends on Json.NET). The downside is that you won't be able to use this for XML.

Hope this helps.

like image 193
Sameer Singh Avatar answered Oct 13 '22 12:10

Sameer Singh


Reading the HttpResponseMessage.Content as a dynamic it's possible but not accessing it directly as dynamic, but using the proper extension method to read it's content as ExpandoObject.

I wrote a behavior test for that, it's clear is a test, but the context is similar to what your question is:

  1. a response object with a json content (I used json in my test)
  2. a dynamic resolution without a model object

The test is structured as:

  1. Given an anonymous object
  2. When creating an HttpResponseMessage with a content of this object using a JsonMedia Formatter
  3. Then it's possible to access it as dynamic using an ExpandoObject

The test prerequisite is to install the Microsoft.AspNet.WebApi.Client So, this is the code of the test

public class RetrieveAnonymousTypeFromTheWebInCSharp 
    : BehaviorTest
{
    private object _testModel;
    private HttpResponseMessage _message;

    protected override void Given()
    {
        _testModel = new
            {
                Id = 14,
                MyProperty = "Test property value"
            };
    }

    protected override void When()
    {
        _message = new HttpResponseMessage(HttpStatusCode.Accepted)
                       {
                           Content =
                               new ObjectContent(_testModel.GetType(),
                                                 _testModel,
                                                 new JsonMediaTypeFormatter
                                                     ())
                       };
    }

    [Test]
    public void Then()
    {
        //then properties could be retrieved back by dynamics
        dynamic content = _message.Content.ReadAsAsync<ExpandoObject>().Result;
        var propertyvalue = content.MyProperty;
        Assert.That(propertyvalue, Is.Not.Null.And.EqualTo("Test property value"));
    }
}

This could be done sure for xml too.

like image 38
Hoghweed Avatar answered Oct 13 '22 12:10

Hoghweed