.NET serialization of JSON data with dynamic structure (schema) - c#

The web service I consume responces with json data.
it gives resultObject as array:
resultObject:[{object1}, {object2},...] if there more then one object
and it returns
resultObject:{object1} if there only one object.
for serializing in .NET I created a "static" structure of classes to map json schema. But if in one case i've got an array (list) of objects an in other case just one object, how is it possible to handle this situation?

I have found a plethora of ugly solutions to this one, but so far goes:
If you use the System.Web.Script.Serialization.JavaScriptSerializer you have very limited control. If the result data type is simple, you could simply use the DeserializeObject method; it will translate everything into Dictionary and the "resultObject" property will in the first case be a Dictionary while the latter case will turn it into an array of such dictionary. It will not save you the headache of the final translation, but you will get the data into dictionaries which could be considered a first step.
I also attempted to use the KnownTypes and the DataContractJsonSerializer, but alas the datacontract serializer needs "hints" in the form of specially named properties to aid it deserializing. (Why am I using the KnownType attribute wrong?). This is a hopeless strategy if you don't have any control of the serialization which I guess is the case for you.
So now we are down to the butt-ugly solutions of which trial-and-error is my first choice:
When using the ScriptSerializer the conversion will fail with an InvalidOperationException if anything is wrong. I then created two data types one with data-as-arrays and one where data is a single instance (the DataClass is my invention since you do not specify the data types):
[DataContract]
public class DataClass
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public int BirthYear { get; set; }
public override string ToString()
{
return "FirstName : '" + FirstName + "', BirthYear: " + BirthYear;
}
}
[DataContract]
public class ResultSingle
{
[DataMember]
public DataClass Data { get; set; }
}
[DataContract]
public class ResultArray
{
[DataMember]
public List<DataClass> Data { get; set; }
}
Using these data types it is possible to translate using
JavaScriptSerializer jSer = new JavaScriptSerializer();
var one = jSer.Deserialize<ResultSingle>(jsonWithSingleInstance);
var many = jSer.Deserialize<ResultArray>(jsonWithArray);
But this of course require you to known the data type in advance and you get two different result types. Instead you could choose to always convert to ResultArray. If you get an exception you should convert as ResultSingle and then instantiate the ResultArray and manually add the data object to the Data list:
static ResultArray ConvertJson(string json)
{
ResultArray fromJson;
JavaScriptSerializer jSer = new JavaScriptSerializer();
try
{
fromJson = jSer.Deserialize<ResultArray>(json);
return fromJson;
}
catch (InvalidOperationException)
{
var single = jSer.Deserialize<ResultSingle> (json);
fromJson = new ResultArray();
fromJson.Data = new List<DataClass>();
fromJson.Data.Add(single.Data);
return fromJson;
}
}
static void Main(string[] args)
{
var jsonWithSingleInstance = "{\"Data\":{\"FirstName\":\"Knud\",\"BirthYear\":1928}}";
var jsonWithArray = "{\"Data\":[{\"FirstName\":\"Knud\",\"BirthYear\":1928},{\"FirstName\":\"Svend\",\"BirthYear\":1930}]}";
var single = ConvertJson(jsonWithSingleInstance);
var array = ConvertJson(jsonWithArray);
}
I do not say this is a beautiful solution (it isn't), but it should do the job.
You could also look at json.net which seem to be the json serializer of choice in .NET: How to install JSON.NET using NuGet?

Well, my service provider finally said that it is really a bug.
http://jira.codehaus.org/browse/JETTISON-102
says that is it because of java version that they use.

Related

Create json in UWP without JSON.Net

I am trying to create a json formatted string using c# in UWP without JSON.Net, but I am just not understanding how to get there. Let's say I wanted to create the following json dynamically:
[{"id":130},{"id":131},{"id":132},{"id":133},{"id":134}]
From everything I have read, it would seem that I need a class that defines the content of my json. For example:
class accountTypes
{
public int id { get; set; }
public string type { get; set; }
}
From there, it would seem that I only need to create a list of type "accountTypes" and then add each "id" to the list.
List<accountTypes> jsonList = new List<accountTypes>();
int numOfChildren = AccountTypesList.Children.Count;
for (int i = 0; i < numOfChildren; i++)
{
if (((CheckBox)AccountTypesList.Children[i]).IsChecked == true)
{
jsonList.Add(new accountTypes() { id = (int)(double)((CheckBox)AccountTypesList.Children[i]).Tag });
}
}
While I am 99% sure that the above code is very flawed, it does not crash on me, so that is a start at least. What I am struggling with now though is how I would serialize the list "jsonList". Everything I have read thus far either points to JSON.net or the JavaScriptSerializer Class, and not Windows.Data.Json. If I could see a simple example on how to serialize json using Windows.Data.Json, then I could at least visualize what is going on with my list and could correct it accordingly. That being said, how do I serialize an array or a list using Windows.Data.Json?
First of all, there's no built-in JSON-serializer that handles all the mapping for you. This is exactly what JSON.NET is doing for you. Therefore, you have to take the manual and long way.
To create exactly this result:
[{"id":130},{"id":131},{"id":132},{"id":133},{"id":134}]
You have to use the JsonArray class. For example, pass your jsonList object to a method like this:
public string ToJson(List<accountTypes> objectList)
{
var jArray = new JsonArray();
foreach (var at in objectList)
{
jArray.Add(ToJson(at));
}
return jArray.ToString();
}
Whereas you use this method to create a JsonObject for your class object itself (as manual step as well):
public JsonObject ToJson(accountTypes at)
{
var jObj = new JsonObject();
jObj.SetNamedValue("id", JsonValue.CreateNumberValue(at.id));
return jObj;
}

Deserializing an unnamed array

I am having difficulty writing the appropriate annotations to represent data which is returned from a JSON Get request which returns data like so:
[{"ProductCode":"0129923083091","Description":"DIESEL ","SalesLitres":6058.7347,"SalesValue":6416.2000},{"ProductCode":"0134039344902","Description":"UNLEADED ","SalesLitres":3489.8111,"SalesValue":3695.7100},
...
]
(ellipsis above just indicating that I could have variable number of these items returned)
In my model class (I am using MVVM approach for a Xamarin project but that's not over relevant here) I am using annotations to represent the model attributes
namespace App8.Models
{
public class ReportRow
{
[JsonProperty("ProductCode")]
public string ProductCode { get; set; } = string.Empty;
[JsonProperty("Description")]
public string Description { get; set; } = string.Empty;
[JsonProperty("SalesLitres")]
public double SalesLitres { get; set; } = 0.0;
[JsonProperty("SalesValue")]
public double SalesValue { get; set; } = 0.0;
}
}
I would like to annote another class which shows the container/contained relationship. However, I coming unstuck as there is no JSON attribute to provide in the annotation to represent the "root" of the returned collection.
I'd have no problem mapping the JSON to an object model for any JSON arrays which are named within the returned JSON. In that case I could create another class with a named JSON attribute which contained a C# List but I am trying to provide an appropriate model mapping for JSON which returns a list of items within an unnamed array.
Any ideas how I could approach this?
To deserialize that JSON, use:
JsonConvert.DeserializeObject<List<ReportRow>>(json)
(or any variant you wish, the key here is asking to deserialize a ICollection of ReportRow. It could be your own class implementing ICollection, or any of the builtins)
The same idea follows to JsonTextReader or whatever other means of deserializing JSON.NET offers. Just use a ICollection<YourType> as target type.

How can I loop through the JSON on Universal Windows Platform with C#

I am developing an app on UWP.
When I connect with a server api and I get the next response I don't have problems.
{"value":"Login successfull","sessionId":"a95077855b05ed0fec5d7fa3abafa126e15aba2a"}
I can get information in the following way:
JsonObject jsonObject = JsonObject.Parse(jsonString);
string token = jsonObject["sessionId"].GetString();
string value = jsonObject["value"].GetString();
but my problem is when i get the next response of the api:
[{"person":{"name":"name1","country":"Spain","city":"user_city","phone":null}},{"person":{"name":"name2","country":"Turkey","city":"user_city","phone":"1111111"}},{"person":{"name":"name3","country":"Argentina","city":"user_city","phone":"22222"}},{"person":{"name":"name4","country":"Argentina","city":"user_city","phone":"33333"}}]
How can I loop through the JSON and get all the people that match a condition?
I have to do with "Windows.Data.Json"
If interested in a solution using only Windows.Data.Json namespace, here it is:
var rootValue = JsonValue.Parse(jsonString);
foreach (var item in rootValue.GetArray())
{
var unamedObject = item.GetObject();
var personObject = unamedObject["person"].GetObject();
System.Diagnostics.Debug.WriteLine(personObject["name"].GetString());
System.Diagnostics.Debug.WriteLine(personObject["country"].GetString());
System.Diagnostics.Debug.WriteLine(personObject["city"].GetString());
System.Diagnostics.Debug.WriteLine(personObject["phone"].GetString());
}
Why would somebody pick Windows.Data.Json over Newtonsoft's Json.net?
If your JSON needs are simple, you can reduce the size of your app ~1 MB by choosing Windows.Data.Json because it is part of the operating system.
I would recommend you try out Json.net nuget package and deserialise the json payload to classes through that.
A good tutorial can be found here: http://windowsapptutorials.com/windows-phone/general/deserialize-json-data-using-newtonsoft-json-net-library/
But if you search you'll find more.
In short, you first copy paste your json and use Visual Studio > File > Paste Special > To paste to classes ( first open an empty cs file and set your cursor inside it ).
After that you use JsonConvert.DeserializeObject<RootObject>() to actually parse the json string.
Once parsed you'll have an array of items if your original json also defined an array.
Note RootObject is the first class object in the generated classes in Visual Studio
There are ways to do it without external libraries, if that is the real reason for the stipulation of Windows.Data.Json.
I'd likely do it something like this...
First I'd make some classes representing the returning JSON:
public class RootObject
{
public Person person { get; set; }
}
public class Person
{
public string name { get; set; }
public string country { get; set; }
public string city { get; set; }
public string phone { get; set; }
}
Then add a little method to deserialize:
public static T Deserialize<T>(string json)
{
var bytes = Encoding.Unicode.GetBytes(json);
using (var ms = new MemoryStream(bytes))
{
var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));
return (T)serializer.ReadObject(ms);
}
}
And finally deserialize and query that result like so:
var persons = Json.Deserialize<List<RootObject>>(textBox.Text);
var peeps = (from p in persons
where p.person.name.StartsWith("name")
select p).ToList();

Separating array and element from a JSON string

I am connecting you Google Places API to retrive results in the form of a JSON string. You can view the complete format of the string Here.
If you a look at it you will see that the actual results array starts after two elements which are html_attributions and next_page_token.
So When i try to deserialize it in this way:
var serializer = new JavaScriptSerializer();
var arr= serializer.Deserialize(result,typeof(string[]));
I get an empty array.
My question is how is there a way i can separate html_attributions and next_page_token fields and the pass the valid results array from the string to be deserialized?
I don't understand the part where you wish to seperate the html_attributions and the next_page_token.
Wouldn't it be sufficient to just deserialize the response with whatever properties that you need?
For example, you can deserialize the response to only retrieve the values that you desire;
// I represent the wrapper result
class Result
{
public List<string> html_attributions { get; set; }
public string next_page_token { get; set; }
public List<ResultItem> results { get; set; }
}
// I represent a result item
class ResultItem
{
public string id { get; set; }
public string name { get; set; }
}
// the actual deserialization
Result Deserialize(string json)
{
var serializer = new JavaScriptSerializer();
return serializer.Deserialize(json, typeof(Result));
}
Edit:
The reason that your deserialization doesn't return you a array of strings is because the response that you retrieve is infact an object and not an array, however the parameter within that object which is named results is an array. In order for you to deserialize more properties you'll have to define them in your "ResultItem" class, sorry for my poor naming here.
For instance, if you'd wish to also retrieve the icon property per result you'll have to add a property named "icon" of type string.
Meanwhile the property "photos" is an array, in order to deserialize it you'll have to create another class and add a property of type list/array of that newly created class, and it has to be named "photos" unless you use a different serializer or use DataContract and DataMember attributes (using the Name property for field mapping).
// the representation of a photo within a result item
class Photo
{
public int height { get; set; }
public List<string> html_attributions { get; set; }
public string photo_reference { get; set; }
public int width { get; set; }
}
// I represent a result item
class ResultItem
{
public string id { get; set; }
public string name { get; set; }
// the added icon
public string icon { get; set; }
// the added photos collection, could also be an array
public List<Photo> photos { get; set; }
}
Just look at the JSON result to figure out what other properties that you might want to add, for instance the "scope" property is an string whilst the "price_level" is an integer.
If I understand your comment correctly you're only interested in the actual results, you'll still have to deserialize the response correctly with its wrapper.
// the actual deserialization
List<ResultItem> Deserialize(string json)
{
var serializer = new JavaScriptSerializer();
var result = serializer.Deserialize(json, typeof(Result));
return result.results;
}
Edit2:
If you really want a string[] as a result you could simply take use of System.Linq using the code above.
string[] stringArray = result.results.Select(r => string.Format("id:{0} - name:{1}", r.id, r.name)).ToArray();
Edit3:
Instead of using the JavascriptSerializer you could use JObject functionality which can be found in the Newtonsoft.Json.Linq library.
var jsonObject = JObject.Parse(json);
string[] results = jsonObject.SelectTokens("results").Select(r => r.ToString()).ToArray();
This will give you an array of strings where each value within the array is the actual json string for each result.
If you however would like to query for the coordinates only:
var jsonObject = JObject.Parse(json);
var coordinates = jsonObject["results"]
.Select(x => x.SelectToken("geometry").SelectToken("location"))
.Select(x => string.Format("{0},{1}", (string)x.SelectToken("lat"), (string)x.SelectToken("lng")))
.ToArray();
This would give you an array of coordinates, eg:
[
"-33.867217,151.195939",
"-33.866786,151.195633",
...
]
Whatever approach you choose you'll be able to accomplish same results using either Newtonsoft or the .net serializer, whilst the Newtonsoft approach would allow you to query without creating strong types for deserialization.
I don't find the point of "[...] pass the valid results array from the string to be deserialized".
Maybe you need to switch to JSON.NET and do something like this:
// You simply deserialize the entire response to an ExpandoObject
// so you don't need a concrete type to deserialize the whole response...
dynamic responseEntity = JsonConvert.DeserializeObject<ExpandoObject>(
googlePlacesJson, new ExpandoObjectConverter()
);
// Now you can access result array as an `IEnumerable<dynamic>`...
IEnumerable<dynamic> results = responseEntity.results;
foreach(dynamic result in results)
{
// Do stuff for each result in the whole response...
}

Manipulate Values of Stringfied values on C#

I have a below code that used JSON.stringify to the object then passed it on POST method (Please see below Javascript code). I'm getting those values on the backend using C#. My problem is, how could I convert/manipulate/access the stringified values. Please see below C# code
Javascript:
var json_db = JSON.stringify(selectedDbInfo);
$.post("../FormActions/DatabaseChanges.aspx", { action: "savedb", orderNumber: orderNumber, selectedDb: json_db},
function (response) {
alert('ok');
});
C#:
var dbValue = c.Request.Params["selectedDb"];
below is the result value of dbValue
"[{\"dbname\":\"BASINS\",\"distance\":\"0\"},{\"dbname\":\"BROWNFIELD\",\"distance\":\"0.5\"},{\"dbname\":\"BRS\",\"distance\":\"0\"}]"
You need to parse the JSON into a .NET array or List.
Many use json.NET for this: http://james.newtonking.com/json
At a push you could use some string manipulation to populate your objects one by one, but I wouldn't recommend that.
There are many samples here on SO.
if you're just want to convert it dictionary look here:
How can I deserialize JSON to a simple Dictionary<string,string> in ASP.NET?
However, there's a built in mechanism in ASP.NET MVC that serializes automatically your json param to predefined objects at your convenient.
You may define a class having the fields like dbname and distance as properties. Then you may deserialize the json string dbValue into a list of that type using NewtonSoft.Json. Please see the code below:
var list = JsonConvert.DeserializeObject<List<RootObject>>(dbValue);
foreach (var item in list)
{
Console.WriteLine(string.Format("dbname: {0}, distance: {1}", item.dbname, item.distance));
}
Ans the definition of RootObject is as simple as you guess:
public class RootObject
{
public string dbname { get; set; }
public string distance { get; set; }
}
Create a custom serializable data contract class, say DatabaseDistance, with following properties:
[DataMember(Name = "dbname")]
private string name;
[DataMember(Name = "distance")]
private double distance;
and use following method for deserialization:
public static T FromJSON<T>(string jsonValue, IEnumerable<Type> knownTypes)
{
//validate input parameters here
T result = default(T);
try
{
using (MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(jsonValue)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T), knownTypes);
result = (T)serializer.ReadObject(stream);
}
}
catch (Exception exception)
{
throw new Exception("An error occurred while deserializing", exception);
}
return result;
}
pass list of your objects as type parameter

Categories

Resources