I'm consuming a WCF service that returns JSON results wrapped inside the 'd' root element. The JSON response looks like this:
{"d":[
{
"__type":"DiskSpaceInfo:#Diagnostics.Common",
"AvailableSpace":38076567552,
"Drive":"C:\\",
"TotalSpace":134789197824
},
{
"__type":"DiskSpaceInfo:#Diagnostics.Common",
"AvailableSpace":166942183424,
"Drive":"D:\\",
"TotalSpace":185149157376
}
]}
I don't want to use dynamic typing, I have my class Diagnostics.Common.DiskSpaceInfo that I want to use when deserializing.
I am using Json.NET (Netwonsoft JSON).
The question is how to tell it to ignore the root element (that 'd' element) and parse what is inside.
The best solution I have so far is to use an anonymous type:
DiskSpaceInfo[] result = JsonConvert.DeserializeAnonymousType(json, new
{
d = new DiskSpaceInfo[0]
}).d;
this actually works but I don't like it very much. Is there another way? What I would like is something like:
DiskSpaceInfo[] result = JsonConvert.Deserialize(json, skipRoot: true);
or something like that...
If you know what to search like in this case "d" which is a root node then you can do the following.
JObject jo = JObject.Parse(json);
DiskSpaceInfo[] diskSpaceArray = jo.SelectToken("d", false).ToObject<DiskSpaceInfo[]>();
If you simply want to ignore the root class which you do not know then you can use the "#Giu Do" solution just that you can use test2.ToObject<DiskSpaceInfo[]>(); instead of the Console.Write(test2);
JObject o = JObject.Parse(json);
if (o != null)
{
var test = o.First;
if (test != null)
{
var test2 = test.First;
if (test2 != null)
{
DiskSpaceInfo[] diskSpaceArray = test2.ToObject<DiskSpaceInfo[]>();
}
}
}
Following on from previous answers here, I would like to suggest using your own static utility class. This is reusable and will allow you to get the syntax you are looking for.
public static class JsonUtil
{
public static T Deserialize<T>(string json, bool ignoreRoot) where T : class
{
return ignoreRoot
? JObject.Parse(json)?.Properties()?.First()?.Value?.ToObject<T>()
: JObject.Parse(json)?.ToObject<T>();
}
}
You would invoke it like this:
var resultA = JsonUtil.Deserialize<DiskSpaceInfo[]>(json, ignoreRoot: true);
or
var resultB = JsonUtil.Deserialize<DiskSpaceInfoRoot>(json, ignoreRoot: false);
By Newtonsoft, I suppose you use JSon.net, here is my solution, I used Linq to JSon available in this framework :
using System;
using Newtonsoft.Json.Linq;
namespace JSonTest
{
class Program
{
static void Main(string[] args)
{
string json = #"{""d"":[
{
""__type"":""DiskSpaceInfo:#Diagnostics.Common"",
""AvailableSpace"":38076567552,
""Drive"":""C:\\"",
""TotalSpace"":134789197824
},
{
""__type"":""DiskSpaceInfo:#Diagnostics.Common"",
""AvailableSpace"":166942183424,
""Drive"":""D:\\"",
""TotalSpace"":185149157376
}
]}";
JObject o = JObject.Parse(json);
if (o != null)
{
var test = o.First;
if (test != null)
{
var test2 = test.First;
if (test2 != null)
{
Console.Write(test2);
}
}
}
Console.Read();
}
}
}
I have used the property First because you need to find the first node after d, which is the first node of the json you received.
You just have to create a function that reproduice the Main, don't forget to check if the objects are not null to avoid NullReferenceException.
Related
I have two Json objects as below need to be compared. I am using Newtonsoft libraries for Json parsing.
string InstanceExpected = jsonExpected;
string InstanceActual = jsonActual;
var InstanceObjExpected = JObject.Parse(InstanceExpected);
var InstanceObjActual = JObject.Parse(InstanceActual);
And I am using Fluent Assertions to compare it. But the problem is Fluent assertion fails only when the attribute count/names are not matching. If the json values are different it passes. I require to fail when values are different.
InstanceObjActual.Should().BeEquivalentTo(InstanceObjExpected);
For example I have the actual and expected json to compare as below. And using the above way of comparing make them Pass which is wrong.
{
"Name": "20181004164456",
"objectId": "4ea9b00b-d601-44af-a990-3034af18fdb1%>"
}
{
"Name": "AAAAAAAAAAAA",
"objectId": "4ea9b00b-d601-44af-a990-3034af18fdb1%>"
}
I did a bit more digging and was able to find out why the OP's test code doesn't run as expected. I was able to fix it by installing and using the FluentAssertions.Json nuget package.
One important thing:
Be sure to include using FluentAssertions.Json otherwise false
positives may occur.
Test code is the following:
using FluentAssertions;
using FluentAssertions.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
[TestFixture]
public class JsonTests
{
[Test]
public void JsonObject_ShouldBeEqualAsExpected()
{
JToken expected = JToken.Parse(#"{ ""Name"": ""20181004164456"", ""objectId"": ""4ea9b00b-d601-44af-a990-3034af18fdb1%>"" }");
JToken actual = JToken.Parse(#"{ ""Name"": ""AAAAAAAAAAAA"", ""objectId"": ""4ea9b00b-d601-44af-a990-3034af18fdb1%>"" }");
actual.Should().BeEquivalentTo(expected);
}
}
Running the test:
Consider using the JToken.DeepEquals() method provided by Newtonsoft. It would look somewhat like this, regardless of which testing framework you're using:
Console.WriteLine(JToken.DeepEquals(InstanceObjActual, InstanceObjExpected));
// false
Made a non-recursive method which will remove twins - idea is to remove same elements from very similar JSONs, so that there will remain only different nodes in each object:
public void RemoveTwins(ref BreadthFirst bf1, ref BreadthFirst bf2) {
JsonNode traversal = bf1.Next();
Boolean removed = false;
do {
if (!removed) {
if (bf2.Current != null) while (bf1.Level == bf2.Level && bf2.Next() != null) ;
if (bf2.Current != null) while (bf1.Level != bf2.Level && bf2.Next() != null) ;
else bf2.Current = bf2.root;
}
else traversal = bf1.Next();
if (bf2.Level < 0) bf2.Current = bf2.Root;
do {
removed = bf1.NextAs(bf1.src, bf2, bf2.src);
if (removed && bf1.Orphan && bf2.Orphan) {
JsonNode same = bf1.Current.Parent;
traversal = bf1.RemoveCurrent();
same = bf2.Current.Parent;
bf2.RemoveCurrent();
bf1.UpdateLevel();
bf2.UpdateLevel();
if (traversal == null
|| bf1.Root == null || bf2.Root == null
|| (bf1.Level == 0 && bf1.Current.NodeBelow == null)) {
traversal = null;
break;
}
} else
if (!removed) {
break;
} else removed = false;
} while (removed);
if (!removed) traversal = bf1.Next();
} while (traversal != null);
}
Complete code + parser on my GitHub (profile or below).
Older CSV version which also sorts input mentioned in my question here How to compare big JSON's? (new one does not, so it could be very slow when one of objects has reversed order - it would be easier to sort during parsing or at least compare both neighbours of twins as first search step)
One option is to deserialize the json strings into C# objects and compare them.
This approach requires more work comparing to using JToken.DeepEquals (as suggested by #JessedeWit), but has the advantage of giving better error messages if your tests fail (see screenshot below).
Your json string can be modelled into the following class:
public class Entity
{
[JsonProperty("Name")]
public string Name { get; set; }
[JsonProperty("objectId")]
public string ObjectId { get; set; }
}
In your test, deserialize the json strings into objects and compare them:
[TestFixture]
public class JsonTests
{
[Test]
public void JsonString_ShouldBeEqualAsExpected()
{
string jsonExpected = #"{ ""Name"": ""20181004164456"", ""objectId"": ""4ea9b00b-d601-44af-a990-3034af18fdb1%>"" }";
string jsonActual = #"{ ""Name"": ""AAAAAAAAAAAA"", ""objectId"": ""4ea9b00b-d601-44af-a990-3034af18fdb1%>"" }";
Entity expectedObject = JsonConvert.DeserializeObject<Entity>(jsonExpected);
Entity actualObject = JsonConvert.DeserializeObject<Entity>(jsonActual);
actualObject.Should().BeEquivalentTo(expectedObject);
}
}
PS: I used NUnit and FluentAssertions in my test method. Running the test:
After you deserialize the json to C# object the correct way is to implement the IComparable interface in the deserialized class and compare the 2 objects.
So:
using System;
using System.Collections.Generic;
class MyObj : IComparable<MyObj>
{
public string Name { get; set; }
public string ObjectID { get; set; }
public int CompareTo(MyObj other)
{
if ((this.Name.CompareTo(other.Name) == 0) &&
(this.ObjectID.CompareTo(other.ObjectID) == 0))
{
return 0;
}
return -1;
}
}
I needed two objects for Audit Logging and I wrote a code like below. It worked great for me.
https://github.com/wbish/jsondiffpatch.net
public static bool DeepCompare(this object obj, object another)
{
var diffObj = new JsonDiffPatch();
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
if (obj.GetType() != another.GetType()) return false;
var objJson = JsonConvert.SerializeObject(obj);
var anotherJson = JsonConvert.SerializeObject(another);
var result = diffObj.Diff(objJson, anotherJson);
return result == null;
}
I'm consuming a WCF service that returns JSON results wrapped inside the 'd' root element. The JSON response looks like this:
{"d":[
{
"__type":"DiskSpaceInfo:#Diagnostics.Common",
"AvailableSpace":38076567552,
"Drive":"C:\\",
"TotalSpace":134789197824
},
{
"__type":"DiskSpaceInfo:#Diagnostics.Common",
"AvailableSpace":166942183424,
"Drive":"D:\\",
"TotalSpace":185149157376
}
]}
I don't want to use dynamic typing, I have my class Diagnostics.Common.DiskSpaceInfo that I want to use when deserializing.
I am using Json.NET (Netwonsoft JSON).
The question is how to tell it to ignore the root element (that 'd' element) and parse what is inside.
The best solution I have so far is to use an anonymous type:
DiskSpaceInfo[] result = JsonConvert.DeserializeAnonymousType(json, new
{
d = new DiskSpaceInfo[0]
}).d;
this actually works but I don't like it very much. Is there another way? What I would like is something like:
DiskSpaceInfo[] result = JsonConvert.Deserialize(json, skipRoot: true);
or something like that...
If you know what to search like in this case "d" which is a root node then you can do the following.
JObject jo = JObject.Parse(json);
DiskSpaceInfo[] diskSpaceArray = jo.SelectToken("d", false).ToObject<DiskSpaceInfo[]>();
If you simply want to ignore the root class which you do not know then you can use the "#Giu Do" solution just that you can use test2.ToObject<DiskSpaceInfo[]>(); instead of the Console.Write(test2);
JObject o = JObject.Parse(json);
if (o != null)
{
var test = o.First;
if (test != null)
{
var test2 = test.First;
if (test2 != null)
{
DiskSpaceInfo[] diskSpaceArray = test2.ToObject<DiskSpaceInfo[]>();
}
}
}
Following on from previous answers here, I would like to suggest using your own static utility class. This is reusable and will allow you to get the syntax you are looking for.
public static class JsonUtil
{
public static T Deserialize<T>(string json, bool ignoreRoot) where T : class
{
return ignoreRoot
? JObject.Parse(json)?.Properties()?.First()?.Value?.ToObject<T>()
: JObject.Parse(json)?.ToObject<T>();
}
}
You would invoke it like this:
var resultA = JsonUtil.Deserialize<DiskSpaceInfo[]>(json, ignoreRoot: true);
or
var resultB = JsonUtil.Deserialize<DiskSpaceInfoRoot>(json, ignoreRoot: false);
By Newtonsoft, I suppose you use JSon.net, here is my solution, I used Linq to JSon available in this framework :
using System;
using Newtonsoft.Json.Linq;
namespace JSonTest
{
class Program
{
static void Main(string[] args)
{
string json = #"{""d"":[
{
""__type"":""DiskSpaceInfo:#Diagnostics.Common"",
""AvailableSpace"":38076567552,
""Drive"":""C:\\"",
""TotalSpace"":134789197824
},
{
""__type"":""DiskSpaceInfo:#Diagnostics.Common"",
""AvailableSpace"":166942183424,
""Drive"":""D:\\"",
""TotalSpace"":185149157376
}
]}";
JObject o = JObject.Parse(json);
if (o != null)
{
var test = o.First;
if (test != null)
{
var test2 = test.First;
if (test2 != null)
{
Console.Write(test2);
}
}
}
Console.Read();
}
}
}
I have used the property First because you need to find the first node after d, which is the first node of the json you received.
You just have to create a function that reproduice the Main, don't forget to check if the objects are not null to avoid NullReferenceException.
I'm trying to understand the included code snippets behavior, it doesn't work as I'd expect. Here is what I'm doing:
Serialize a LINQ object to JSON
Deserialize the JSON at (1) back in its initial type
Validate that each property are still in the Object
I serialize the object from (2) back to JSON
Print the json from (4) and visually inspect it
My issue is that at (5), any LINQ entity that is a property of the main LINQ object is gone, even tho it was still there when on validation at (3). All my LINQ classes are LINQ to SQL. I am using Newtons JSON.Net library. I tried the same type of logic with non-linq object and the defect seen at (5) doesn't occur.
var infoId = Guid.NewGuid();
var alertId = Guid.NewGuid();
var info = new Info();
info.InfoId = infoId;
var alert = new Alert();
alert.AlertId = alertId;
alert.Infos.Add(info);
var json = JsonConvert.SerializeObject(alert);
Debug.WriteLine(json); //All looking good, nothing missing
var deserializedObject = JsonConvert.DeserializeObject<Alert>(json);
Assert.AreEqual(alertId, deserializedObject.AlertId); //Assert is valid
Assert.AreEqual(infoId, deserializedObject.Infos[0].InfoId); //Assert is valid
var json2 = JsonConvert.SerializeObject(deserializedObject);
Debug.WriteLine(json2); //Infos is gone
Update:
I have done some debbugging, and in the Serialization of deserializedObject
var json2 = JsonConvert.SerializeObject(deserializedObject);
I see the following when reaching the Info Serialization step (next code snipped):
this.serializing is true
this._Infos.HasLoadedOrAssignedValues is false
The get return null is called. (get returns the null value)
If I put a breakpoint, and put my cursor on the return this._Infos I actually see the object that It should return ...
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Alert_Info", Storage="_Infos", ThisKey="AlertId", OtherKey="AlertId")]
[global::System.Runtime.Serialization.DataMemberAttribute(Order=15, EmitDefaultValue=false)]
public EntitySet<Info> Infos
{
get
{
if ((this.serializing && (this._Infos.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._Infos;
}
set
{
this._Infos.Assign(value);
}
}
Update:
This leads me to believe that I might need to find a way to modify the default value of HasLoadedOrAssignedValues when it is being deserialized from Newtons Json.Net.
I also found a dirty fix, which I don't really like that seems to be working:
var deserializedObject = JsonConvert.DeserializeObject<EasAlert>(json);
var list = new List<EasInfo>();
deserializedObject.EasInfos.SetSource(list); //<--- Fix, the Info will show up in json2
Assert.AreEqual(alertId, deserializedObject.AlertId);
Assert.AreEqual(infoId, deserializedObject.EasInfos[0].InfoId);
I was trying Microsoft's JavaScriptSerializer, and it worked fine. Code and output can be found below.
Could it be a bug in Newton's?
void Main()
{
var infoId = Guid.NewGuid();
var alertId = Guid.NewGuid();
var info = new Info();
info.InfoId = infoId;
var alert = new Alert();
alert.AlertId = alertId;
alert.Infos.Add(info);
var jss = new JavaScriptSerializer();
var json = jss.Serialize(alert); //JsonConvert.SerializeObject(alert);
Debug.WriteLine(json); //All looking good, nothing missing
var deserializedObject = jss.Deserialize<Alert>(json); //JsonConvert.DeserializeObject<Alert>(json);
(alertId == deserializedObject.AlertId).Dump(); //Assert is valid
(infoId == deserializedObject.Infos[0].InfoId).Dump(); //Assert is valid
var json2 = jss.Serialize(deserializedObject); //JsonConvert.SerializeObject(deserializedObject);
Debug.WriteLine(json2); //Infos is gone - NOT GONE!
}
public class Alert
{
public Guid AlertId { get; set; }
public List<Info> Infos { get; set; }
public Alert()
{
Infos = new List<Info>();
}
}
public class Info
{
public Guid InfoId { get; set; }
}
Output:
{"AlertId":"0340e855-065c-4ac7-868e-5999fa4b7862","Infos":[{"InfoId":"31e269a1-4354-423d-84bc-62f6dc06b10f"}]}
True
True
{"AlertId":"0340e855-065c-4ac7-868e-5999fa4b7862","Infos":[{"InfoId":"31e269a1-4354-423d-84bc-62f6dc06b10f"}]}
check Alert class (constructor)
public class Alert{
public Guid AlertId{get;set;}
public List<Info> Infos {get;set;}
public Alert() {
Infos = new List<Info>();
}
}
looks like you are missing List init
even with EntitySet
i have a json response like this:
{"response_values":[110,{"id":14753,"name":"piter"},{"id":14753,"name":"rabbit"}]}
and i have a simple class
public class Class1
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
and when i trying to convert json to list of the objects with this method:
public T Cast<T>(string json)
{
var result = default(T);
var jsonObject = JObject.Parse(json);
if (jsonObject != null)
{
var responseToken = jsonObject["response"];
result = responseToken.ToObject<T>();
}
return result;
}
like this
...
var jsonString = GetJson();
var items = Cast<List<Class1>>();
...
i have an exceiption, because value "110" is integer. How can i skip this value?
You always have this option if you expect the values to ignore to always be at the beginning:
if (jsonObject != null)
{
var responseToken = parsed["response_values"].SkipWhile(j => j.Type != JTokenType.Object);
if (responseToken.Count() > 0) result = responseToken.ToObject<T>();
}
You may prefer to use Skip(1) instead of SkipWhile if it's always the first value. Alternatively, you can use Where to ignore or select tokens anywhere in the message.
Of course, you can play around with this approach (changing things) depending on exactly what you expect to be returned in success scenarios.
I´m parsing a JSON string to a corresponding .NET Object with the Newtonsoft library. I have a problem parsing JSON properties that are arrays. Sometimes the JSON property is an array, other times, it is a single element.
Example:
This is the .NET object:
public class xx
{
public string yy { get; set; }
public List<string> mm{ get; set; }
}
When i receive this JSON:
{ "xx": {"yy":"nn", "mm": [ "zzz", "aaa" ] } }
I perfectly can do:
JsonConvert.DeserializeObject<xx>(json);
But sometimes I receive this JSON:
{ "xx": {"yy":"nn", "mm":"zzz"} }
And the deserialization fails because of the list property on the C# object.
How can I define an object for deserialize the two JSON string in the same object (with List<string>).
-------- UPDATE -----
First of all a WS generate a XML doing some operation.. the XML is like
<xx yy='nn'><mm>zzz</mm></xx>
and if there are more elements is:
<xx yy='nn'><mm>zzz</mm><mm>aaa</mm></xx>
finally the WS convert this XML doing:
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
var json = JsonConvert.SerializeXmlNode(doc);
and send to me the json.. and here begins my problem..
Updated Answer:
Looking at how JSON.Net maps XML, it takes the approach that what it sees is what it serializes, except that if it sees multiples, it will make an array. This is great for many XML DOM trees with consistent layout, but unfortunately cannot work for your purposes.
You can verify this by looking at the body for the functions SerializeGroupedNodes() and SerializeNode() in the following file source.
XmlNodeConverter.cs source code # CodePlex, ChangeSet #63616
There's another option that I'd previously thought might be overkill but would be helpful now that we know what to expect from the default behavior on the serializing end.
Json.Net supports using custom converters derived from JsonConverter to map particular cases of values on a case-by-case basis.
We can handle this either at the serializing side or the deserializing side. I've chosen to write a solution on the deserializing side, as you probably have other existing reasons to map XML to JSON.
One great benefit is that your class can stay intact except for the override, which requires that you apply an attribute. Here's a code sample demonstrating how to use JsonAttribute and a custom converter class (MMArrayConverter) to fix your problem. Note that you will probably want to test this more thoroughly and maybe update the converter to handle other cases, say if you eventually migrate to IList<string> or some other funky case like Lazy<List<string>>, or even make it work with generics.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
namespace JsonArrayImplictConvertTest
{
public class MMArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.Equals(typeof(List<string>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
List<string> parseList = new List<string>();
do
{
if (reader.Read())
{
if (reader.TokenType == JsonToken.String)
{
parseList.Add((string)reader.Value);
}
else
{
if (reader.TokenType == JsonToken.Null)
{
parseList.Add(null);
}
else
{
if (reader.TokenType != JsonToken.EndArray)
{
throw new ArgumentException(string.Format("Expected String/Null, Found JSON Token Type {0} instead", reader.TokenType.ToString()));
}
}
}
}
else
{
throw new InvalidOperationException("Broken JSON Input Detected");
}
}
while (reader.TokenType != JsonToken.EndArray);
return parseList;
}
if (reader.TokenType == JsonToken.Null)
{
// TODO: You need to decide here if we want to return an empty list, or null.
return null;
}
if (reader.TokenType == JsonToken.String)
{
List<string> singleList = new List<string>();
singleList.Add((string)reader.Value);
return singleList;
}
throw new InvalidOperationException("Unhandled case for MMArrayConverter. Check to see if this converter has been applied to the wrong serialization type.");
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Not implemented for brevity, but you could add this if needed.
throw new NotImplementedException();
}
}
public class ModifiedXX
{
public string yy { get; set; }
[JsonConverter(typeof(MMArrayConverter))]
public List<string> mm { get; set; }
public void Display()
{
Console.WriteLine("yy is {0}", this.yy);
if (null == mm)
{
Console.WriteLine("mm is null");
}
else
{
Console.WriteLine("mm contains these items:");
mm.ForEach((item) => { Console.WriteLine(" {0}", item); });
}
}
}
class Program
{
static void Main(string[] args)
{
string jsonTest1 = "{\"yy\":\"nn\", \"mm\": [ \"zzz\", \"aaa\" ] }";
ModifiedXX obj1 = JsonConvert.DeserializeObject<ModifiedXX>(jsonTest1);
obj1.Display();
string jsonTest2 = "{\"yy\":\"nn\", \"mm\": \"zzz\" }";
ModifiedXX obj2 = JsonConvert.DeserializeObject<ModifiedXX>(jsonTest2);
obj2.Display();
// This test is now required in case we messed up the parser state in our converter.
string jsonTest3 = "[{\"yy\":\"nn\", \"mm\": [ \"zzz\", \"aaa\" ] },{\"yy\":\"nn\", \"mm\": \"zzz\" }]";
List<ModifiedXX> obj3 = JsonConvert.DeserializeObject<List<ModifiedXX>>(jsonTest3);
obj3.ForEach((obj) => { obj.Display(); });
Console.ReadKey();
}
}
}
Original Answer:
It would be best to fix the JSON you're receiving at the source, as many have already pointed out. You may wish to post an update showing how the XML in your updated comment is being mapped to JSON, as that would be the best route overall.
However, if you find that this is not possible and you want some way to serialize and handle the variant value after-the-fact, you can patch things up by declaring mm to be type object, and then handling the possible cases yourself using JSON.Net's Linq support. In the two scenarios you described, you'll find that declaring mm to be type object will result in either a null, a string, or a JArray being assigned to mm by the call to DeserializeObject<>.
Here's a code sample that shows this in action. There's also a case in other circumstances where you could receive a JObject, which is also covered in this sample. Note that the member function mmAsList() does the work of patching up the difference for you. Also note that I've handled null here by returning a null for List<string>; you will probably want to revise this for your implementation.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace JsonArrayUnionTest
{
public class ModifiedXX
{
public string yy { get; set; }
public object mm { get; set; }
public List<string> mmAsList()
{
if (null == mm) { return null; }
if (mm is JArray)
{
JArray mmArray = (JArray)mm;
return mmArray.Values<string>().ToList();
}
if (mm is JObject)
{
JObject mmObj = (JObject)mm;
if (mmObj.Type == JTokenType.String)
{
return MakeList(mmObj.Value<string>());
}
}
if (mm is string)
{
return MakeList((string)mm);
}
throw new ArgumentOutOfRangeException("unhandled case for serialized value for mm (cannot be converted to List<string>)");
}
protected List<string> MakeList(string src)
{
List<string> newList = new List<string>();
newList.Add(src);
return newList;
}
public void Display()
{
Console.WriteLine("yy is {0}", this.yy);
List<string> mmItems = mmAsList();
if (null == mmItems)
{
Console.WriteLine("mm is null");
}
else
{
Console.WriteLine("mm contains these items:");
mmItems.ForEach((item) => { Console.WriteLine(" {0}", item); });
}
}
}
class Program
{
static void Main(string[] args)
{
string jsonTest1 = "{\"yy\":\"nn\", \"mm\": [ \"zzz\", \"aaa\" ] }";
ModifiedXX obj1 = JsonConvert.DeserializeObject<ModifiedXX>(jsonTest1);
obj1.Display();
string jsonTest2 = "{\"yy\":\"nn\", \"mm\": \"zzz\" }";
ModifiedXX obj2 = JsonConvert.DeserializeObject<ModifiedXX>(jsonTest2);
obj2.Display();
Console.ReadKey();
}
}
}
What the sending service sends is supposed to conform to a contract. If it doesn't, then well, either you beat up the sending developer and make them fix it, or the various things that are sent to you are the contract. A pity you don't have any metadata to know for sure, you'll just have to try a variety of contracts until one works.
object someValue;
try
{
someValue =JsonConvert.DeserializeObject<TypeWithList>(json);
}
catch
{
try
{
someValue = JsonConvert.DeserializeObject<TypeWithString>(json);
}
catch
{
//Darn, yet another type
}
}
In your case you can directly use the static method from JsonConvert class
PopulateObject(string value, object target, JsonSerializerSettings settings);
pass the JsonSerializerSettings object as
new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.All})
I think you need to look at your Javascript object. If you explicitly declare the type of the properties of the object that you are going to serialize into JSON you shouldn't run into any inconsistencies.
var stringProperty = new String();
var arrayProperty = new Array();
// Assign value to stringProperty and push elements into arrayProperty
var object = {
stringProperty: stringProperty,
arrayProperty: arrayProperty
};
var jsonObject = JSON.stringify(object);
document.write(jsonObject);
If you look at the output you will see that arrayProperty is always serialized into an array whether there are zero, one or many elements.