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?
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.
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.
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.
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.
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:
The test is structured as:
HttpResponseMessage
with a content of this object using a JsonMedia FormatterExpandoObject
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With