I need to deserialize
{'Id': 'id123',
'Time': 1436231503,
'Name': 'foo',
'ProductId': 1}
into Container1
public class Container1
{
public CommonFields Common { get; set; }
//fields specific to Container1
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "ProductId")]
public int ProductId { get; set; }
}
and
{
'Id': 'id123',
'Time': 1436231503,
'Group':'10768C21-9971-4D2F-ACD7-10C2EF19FCA8'
}
into Container2
public class Container2
{
public CommonFields Common { get; set; }
//fields specific to Container2
[JsonProperty(PropertyName = "Group")]
public Guid Group { get; set; }
}
using composition (not inheritance). Both JSON have 2 common fields (Id and Time) and specific fields.
With newtonsoft.json
JsonConvert.DeserializeObject<Container1>(json_container1)
the result is that the properties of the 2 container are correctly deserialized. The common properties of the composed classed are not deserialized.
How can I deserialize JSON into C# classes that use only composition?
(using newtonsoft.json is not compulsory)
Below is my attempt.
public class CommonFields
{
[JsonProperty(PropertyName = "Id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "Time")]
public long Time { get; set; }
}
public class Container1
{
public CommonFields Common { get; set; }
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "ProductId")]
public int ProductId { get; set; }
}
public class Container2
{
public CommonFields Common { get; set; }
[JsonProperty(PropertyName = "Group")]
public Guid Group { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
string json_container1 = #"{
'Id': 'id123',
'Time': 1436231503,
'Name': 'foo',
'ProductId': 1
}";
string json_container2 = #"{
'Id': 'id123',
'Time': 1436231503,
'Group':'10768C21-9971-4D2F-ACD7-10C2EF19FCA8'
}";
var container1Obj = JsonConvert.DeserializeObject<Container1>(json_container1);
var container2Obj = JsonConvert.DeserializeObject<Container2>(json_container2);
Console.ReadKey();
}}}
Don't do it.
The JSON element you deserilize from should not be change, you can remove some properties but it's a bad practice to change its properties structure.
JSON file\content should have a compatible JSON class, if you want to make any changes, make another custom class and make a mapping logic between them.
I think you can just do the deserialize again just on CommonFields of two objects.
container1Obj.Common = JsonConvert.DeserializeObject<CommonFields>(json_container1);
container2Obj.Common = JsonConvert.DeserializeObject<CommonFields>(json_container2);
I know you want to use composition, but I really can't see any pros using composition over inheritance here.
public class BaseClass
{
[JsonProperty(PropertyName = "Id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "Time")]
public long Time { get; set; }
}
public class Container1 : BaseClass
{
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "ProductId")]
public int ProductId { get; set; }
}
public class Container2 : BaseClass
{
[JsonProperty(PropertyName = "Group")]
public Guid Group { get; set; }
}
This is as simple as it can get and should get the job done.
Your question basically the reverse of the question Can I serialize nested properties to my class in one operation with Json.net?, and can be solved with a similar strategy. To simplify things, create an interface for all classes containing common fields:
public interface IHasCommonFields
{
CommonFields Common { get; set; }
}
Then you can create the following generic converter for any type implementing this interface:
public class HasCommonFieldsConverter<T> : JsonConverter where T : IHasCommonFields
{
[ThreadStatic]
static bool disabled;
// Disables the converter in a thread-safe manner.
bool Disabled { get { return disabled; } set { disabled = value; } }
public override bool CanWrite { get { return !Disabled; } }
public override bool CanRead { get { return !Disabled; } }
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
if (token == null || token.Type == JTokenType.Null)
return null;
using (new PushValue<bool>(true, () => Disabled, val => Disabled = val)) // Prevent infinite recursion of converters
{
var hasCommon = token.ToObject<T>(serializer);
var common = (hasCommon.Common ?? (hasCommon.Common = new CommonFields()));
serializer.Populate(token.CreateReader(), common);
return hasCommon;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
{
writer.WriteNull();
return;
}
using (new PushValue<bool>(true, () => Disabled, val => Disabled = val)) // Prevent infinite recursion of converters
{
var hasCommon = (T)value;
var obj = JObject.FromObject(hasCommon, serializer);
var common = hasCommon.Common;
if (common != null)
{
var commonObj = JObject.FromObject(common, serializer);
obj.Merge(commonObj);
}
obj.WriteTo(writer);
}
}
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
Then apply it to all classes implementing IHasCommonFields - and in addition mark the Common property with JsonIgnore:
[JsonConverter(typeof(HasCommonFieldsConverter<Container1>))]
public class Container1 : IHasCommonFields
{
[JsonIgnore]
public CommonFields Common { get; set; }
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "ProductId")]
public int ProductId { get; set; }
}
[JsonConverter(typeof(HasCommonFieldsConverter<Container2>))]
public class Container2 : IHasCommonFields
{
[JsonIgnore]
public CommonFields Common { get; set; }
[JsonProperty(PropertyName = "Group")]
public Guid Group { get; set; }
}
Then, to test:
public class TestClass
{
public static void Test()
{
var container1 = new Container1 { Name = "name", ProductId = 101, Common = new CommonFields { Id = "1401", Time = DateTime.Today.Ticks } };
var container2 = new Container2 { Group = Guid.NewGuid(), Common = new CommonFields { Id = "2401", Time = DateTime.Today.Ticks } };
Test(container1);
Test(container2);
}
private static void Test<T>(T container) where T : class, IHasCommonFields
{
var json = JsonConvert.SerializeObject(container, Formatting.Indented);
Debug.WriteLine(json);
var containerback = JsonConvert.DeserializeObject<T>(json);
var json2 = JsonConvert.SerializeObject(containerback, Formatting.Indented);
Debug.Assert(json == json2); // No assert
if (container.Common != null)
{
Debug.Assert(container.Common.Id == containerback.Common.Id); // No assert
Debug.Assert(container.Common.Time == containerback.Common.Time); // No assert
}
}
}
The JSON created looks like:
{
"Name": "name",
"ProductId": 101,
"Id": "1401",
"Time": 635725152000000000
}
And
{
"Group": "9ed31118-c0b7-4d9f-8f57-303b2e164643",
"Id": "2401",
"Time": 635725152000000000
}
The one disadvantage of this converter is that, if the Common property is null when serialized, it will be deserialized non-null with default values.
If you don't want the IHasCommonFields interface, you could e.g. create an abstract generic base class for your converter with abstract methods to get and set the common fields, then override those methods in each subclass.
(Honestly inheritance does seem simpler than composition here, as other answers have stated.)
Related
I'm currently dealing with getting data from an external API. The data I receive looks something like what is shown below. (Just a mockup; don't expect the values to make any sense. It's just to illustrate what kind of data I get.)
{
"user": [
{
"key": "12345678",
"data": [
{
"id": "Name",
"string": "Bob"
},
{
"id": "ElapsedTimeSinceLastMessage",
"timestamp": 1618233964000
},
{
"id": "Age",
"number": 27
}
]
}
]
}
I don't really know how I should be going about deserializing this JSON.
The classes I'm using to deserialize right now look like this:
public class User
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("data")]
public List<DataEntry> DataEntries { get; set; }
}
public class DataEntry
{
[JsonProperty("id")]
public string Id { get; set; }
public Type Value { get; set; }
}
And I don't know what I need to set in order to deserialize the Value inside the DataEntry. Maybe someone can guide me into the right direction?
The Data part of this JSON is really just a Dictionary<string, object> in disguise. You can use a custom JsonConverter to transform the list of id/value pairs into that format for easy use.
Frist, define these classes:
class RootObject
{
[JsonProperty("user")]
public List<User> Users { get; set; }
}
class User
{
public string Key { get; set; }
[JsonConverter(typeof(CustomDataConverter))]
public Dictionary<string, object> Data { get; set; }
}
Next, define the converter:
class CustomDataConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Dictionary<string, object>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JToken.Load(reader)
.Children<JObject>()
.ToDictionary(jo => (string)jo["id"],
jo => jo.Properties()
.Where(jp => jp.Name != "id" && jp.Value is JValue)
.Select(jp => ((JValue)jp.Value).Value)
.FirstOrDefault());
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
You can then deserialize and dump out the data like this:
var root = JsonConvert.DeserializeObject<RootObject>(json);
foreach (User user in root.Users)
{
Console.WriteLine("User Key: " + user.Key);
foreach (var kvp in user.Data)
{
Console.WriteLine(kvp.Key + ": " + kvp.Value);
}
}
Here is a working demo: https://dotnetfiddle.net/GIT4dl
One angle of attack would be with Dictionaries:
public class WithUser
{
public List<User> User { get; set; }
}
public class User
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("data")]
public List<Dictionary<string,object>> DataEntries { get; set; }
}
The extraction is a bit of a pain but possible:
public static void Main()
{
var json = File.ReadAllText("Example.json");
var x = JsonConvert.DeserializeObject<WithUser>(json);
var user = x.User.Single();
var age = Extract<long>(user, "Age");
var name = Extract<string>(user, "Name");
var elapsedTimeSinceLastMessage = TimeSpan.FromTicks(Extract<long>(user, "ElapsedTimeSinceLastMessage"));
}
public static T Extract<T>(User user, string name)
{
var o = user.DataEntries
.SingleOrDefault(d => (string)d["id"] == name) // Find the one with age
.SingleOrDefault(kvp => kvp.Key != "id") // Find the not 'id' value
.Value; // Take the value
return (T)o;
}
The main issue is in the models that you created.
First of all, base on the JSON, you need another class that contains a list of Users.
public class ResultClass
{
public List<User> User { get; set; }
}
After that, because the second object of data property has not a constant name, we can't specify a constant name for it (like value). We should define the data property as an object. So the user class should be like this:
public class User
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("data")]
public List<object> DataEntries { get; set; }
}
In the end, in the controller, you should deserialize the ResultJson class:
var result = JsonConvert.DeserializeObject<ResultClass>(jsonTxt);
You can use Json to C#. It generates following classes from your json string. As you can see, you can also use nullable types(long?, int?). If there is a value, sets the required variable. Otherwise leaves it as null. In this way, you can get your different type according to id of data.
public class DataEntry
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("string")]
public string String { get; set; }
[JsonProperty("timestamp")]
public long? Timestamp { get; set; }
[JsonProperty("number")]
public int? Number { get; set; }
}
public class User
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("data")]
public List<DataEntry> Data { get; set; }
}
public class Root
{
public List<User> User { get; set; }
}
To Deserialize:
string response = "{\"user\":[{\"key\":\"12345678\",\"data\":[{\"id\":\"Name\",\"string\":\"Bob\"},{\"id\":\"ElapsedTimeSinceLastMessage\",\"timestamp\":1618233964000},{\"id\":\"Age\",\"number\":27}]}]}";
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(response);
I have these interfaces:
public interface IParameter
{
string Name { get; }
object UntypedValue { get; set; }
}
public interface IValidationPolicy<T>
{
bool Validate(T toValidate);
T Default();
}
A parameter base class
[Serializable]
public abstract class ParameterBase : IParameter
{
public abstract string Name { get; protected set; }
public abstract object UntypedValue { get; set; }
}
A parameter concrete class (I have more but them are quite similar):
public class Parameter<T, V> : ParameterBase where V : IValidationPolicy<T>
{
[XmlAttribute("Name")]
public override string Name { get; protected set; }
[XmlIgnore]
protected V validation_policy_;
[XmlElement("AnyValidation", Type = typeof(AnyValidation<>))]
[XmlElement("MultiOptionsValidation", Type = typeof(MultiOptionsValidation<>))]
[XmlElement("RangeValidation", Type = typeof(RangeValidation<>))]
[XmlElement("TextValidation", Type = typeof(TextValidation))]
public V Validation
{
get
{
return validation_policy_;
}
}
[XmlIgnore]
protected T value_;
[XmlElement("Value")]
public T Value
{
get
{
return value_;
}
set
{
if (validation_policy_.Validate(value))
{
value_ = value;
}
}
}
[XmlIgnore]
public object UntypedValue
{
get
{
return Value;
}
set
{
throw new NotImplementedException();
}
}
}
And an XMLParameter class:
public class XMLParameter : INotifyPropertyChanged
{
public string Description { get; set; }
public int PasswordLevel { get; set; }
public bool Enabled { get; set; }
public ParameterBase Parameter { get; set; }
}
How can I serialize and deserialize a list of XMLParameters?
In particular I have problem on serializing the IParameter objects.
Since the interface is not serializable as first attempt I created a base abstract class ParameterBase and derive the Parameter from it.
But when I try to serialize it in a test method:
var validation = new RangeValidation<int>() { MinValue = 1, MaxValue = 6 };
var parameter = new Parameter<int, RangeValidation<int>>();
parameter.Initialize("NumberOfTrays", validation);
parameter.Value = 6;
XElement par = validation.ToXElement<Parameter<int, RangeValidation<int>>>();
I got an exception: Error at reflection of type 'ConfigurableLibray.Parameter'2[System.Int32,ConfigurableLibray.RangeValidation'1[System.Int32]]'
The inner exception says that ConfigurableLibray.Parameter'2[T,V] is not supported
What am I doing wrong?
Thanks in advance for any suggestion!
I solved implementing manually the serialization and deserialization of the classes using reflection.
I would like to use JavaScriptSerializer for this because I'm afraid of how many things might break and I can't change anything on the client-side.
If Json.Net is the best way to go then I will try it but I need an example.
I have this class
Class Definition
[DataContract]
[Serializable]
public class Family
{
[DataMember(Order = 0)]
public List<Member> members { get; set; }
}
[DataContract]
[Serializable]
public class Member
{
[DataMember(Order = 0)]
public string FName { get; set; }
[DataMember(Order = 1)]
public string LName { get; set; }
[DataMember(Order = 2)]
public string DOB { get; set; }
[DataMember(Order = 3)]
public string Gender { get; set; }
[DataMember(Order = 4)]
public string Type { get; set; }
}
The JSON I am Deserializing looks like this
JSON Example
[
{
"Family": [
{
"FName": "Jane",
"LName": "Prospect",
"DOB": "04/01/1980",
"Gender": "Female",
"Type": "Adult"
},
{...}
]
},
{
"OptionChoice": 34,
"OptionText": "Aquatics"
},
{...},
{...}
]
I can deserialize the Answer objects fine (OptionChoice,OptionText).
Hower the answer object has an additional item full of nulls where it is parsing the Family section of the JSON. I don't really want that.
When I try to deserialize the Family part I get an error
Type 'Family' is not supported for deserialization of an array.
It says it has a null for the Family.members. Is it looking for "Family": [ "members": {...},{...}] ?
How can I get this working without changing the JSON example?
Update After dbc Answer:
Here is my model
[DataContract]
[Serializable]
public class Answer
{
[DataMember(Order = 2, EmitDefaultValue = false)]
public int FormID { get; set; }
[DataMember(Order = 3,EmitDefaultValue = false)]
public int Question { get; set; }
[DataMember(Order = 5)]
public int OptionChoice { get; set; }
[DataMember(Order = 6,IsRequired = false)]
public string OptionText { get; set; }
[DataMember(Order = 5, EmitDefaultValue = false)]
public bool lockAnswer { get; set; }
[DataMember(Order = 1,EmitDefaultValue= false)]
public List<FamilyMember> Family { get; set; }
}
[DataContract]
[Serializable]
public class FamilyMember
{
[DataMember(Order = 0)]
public string FName { get; set; }
[DataMember(Order = 1)]
public string LName { get; set; }
[DataMember(Order = 2)]
public string DOB { get; set; }
[DataMember(Order = 3)]
public string Gender { get; set; }
[DataMember(Order = 4)]
public string Type { get; set; }
}
At this point in my creation of my SO update I solved the problem with Rubber Duck Debugging
Turns out I had some regex that was stripping all "[" & "]" then manually adding them back but only on the ends.
This cool comparison tool at http://pro.jsonlint.com/ helped btw.
Thanks! If anybody in the future reads this and wants to show a testable way to do this in JSON.net (I tried but got stuck) please do.
Your question includes two related questions:
Exception loading the Family list.
The problem here is that, as you suspect, there is no property corresponding to members. What your JSON has is an array of objects, each of which might have an array-valued property Family. Thus your data model should look like:
public class ResponseItem
{
public int? OptionChoice { get; set; }
public string OptionText { get; set; }
public List<FamilyMember> Family { get; set; }
// Other fields not shown from {...}
}
public class FamilyMember
{
public string FName { get; set; }
public string LName { get; set; }
public string DOB { get; set; }
public string Gender { get; set; }
public string Type { get; set; }
}
You state, "Hower the answer object has an additional item full of nulls where it is parsing the Family section of the JSON. I don't really want that." This could be done by deserializing the JSON into a polymorphic array where each possible derived type has just the minimal number of fields. However, since there is no __type information in your JSON, you will need to add some slightly fussy logic to chose the correct concrete type for each array element. Your data model would look like:
public interface IResponseItem // base interface for all possible responses
{
}
public class FamilyResponse : IResponseItem
{
public List<FamilyMember> Family { get; set; }
}
public class OptionsResponse : IResponseItem
{
public int OptionChoice { get; set; }
public string OptionText { get; set; }
}
In complex serialization situations like this people seem to prefer Json.NET, nevertheless it's still possible with JavaScriptSerializer as per your question. You must code up a JavaScriptConverter to select the appropriate derived type from the base IResponseItem type by matching the property names, for instance:
public class PolymorphicTypeConverter : JavaScriptConverter
{
public Type BaseType { get; private set; }
public Type[] DerivedTypes { get; private set; }
public PolymorphicTypeConverter(Type baseType, IEnumerable<Type> derivedTypes)
{
this.BaseType = baseType;
this.DerivedTypes = derivedTypes.ToArray();
}
static MemberInfo FindMember(Type type, string name)
{
try
{
var propInfo = type.GetProperty(name,
BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
if (propInfo != null
&& propInfo.GetSetMethod() != null
&& propInfo.GetIndexParameters().Length == 0)
return propInfo;
var fieldInfo = type.GetField(name,
BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
if (fieldInfo != null)
return fieldInfo;
}
catch (AmbiguousMatchException)
{
return null;
}
return null;
}
IEnumerable<Type> AncestorsAndSelf(Type type)
{
for (; type != null; type = type.BaseType)
if (DerivedTypes.Contains(type))
yield return type;
}
Type FindUniqueTypeMatch(IDictionary<string, object> jsonProperties)
{
List<Type> matches = new List<Type>();
foreach (var type in DerivedTypes)
{
if (type.IsInterface)
continue; // Bug?
bool isMatch = true;
foreach (var name in jsonProperties.Keys)
{
if (FindMember(type, name) == null)
{
isMatch = false;
break;
}
}
if (isMatch)
{
matches.Add(type);
}
}
if (matches.Count == 0)
return null;
else if (matches.Count == 1)
return matches[0];
else
{
// Multiple matches.
// If there is a common base type to all matches, return it. Otherwise, give up.
var candidates = AncestorsAndSelf(matches[0]).Reverse();
foreach (var match in matches.Skip(1))
{
candidates = candidates.Zip(AncestorsAndSelf(match).Reverse(), (t1, t2) => (t1 == t2 ? t1 : null)).Where(t => t != null);
}
return candidates.LastOrDefault();
}
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var subtype = FindUniqueTypeMatch(dictionary);
if (subtype == null)
throw new JsonSerializationException();
var method = serializer.GetType().GetMethod("ConvertToType");
var generic = method.MakeGenericMethod(subtype);
return generic.Invoke(serializer, new object [] { dictionary } );
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
// Should never be called.
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new Type[] { BaseType };
}
}
}
This only works when each object in your JSON array has properties that match one and only one type in the derived type array. If this cannot be guaranteed, because, for instance, null fields were not serialized leading to multiple matches, you will need to enhance the converter to make a best guess match.
Then call it like:
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new PolymorphicTypeConverter(typeof(IResponseItem), new Type[] { typeof(FamilyResponse), typeof(OptionsResponse) }) });
var responseArray = serializer.Deserialize<IResponseItem[]>(json);
I have following JSON
string json = #"[[{""campaignId"":201410018,""programCode"":""54321""},{""reason"":201410018,""about"":""54321""}],[{""campaignId"":201410019,""programCode"":""54322""},{""reason"":201410018,""about"":""54321""}]]"
And i have created following class
public class JSONResponse
{
public number[] number{ get; set; }
public Inf[] inf{ get; set; }
}
public class number
{
public int reason { get; set; }
public string about { get; set; }
}
public class Inf
{
public int campaignId { get; set; }
public string programCode { get; set; }
}
to deserialize i am calling below code
List<List<JSONResponse>> myDeserializedObjList = JsonConvert.DeserializeObject<List<List<JSONResponse>>>(jsonstr);
But still both of my class data is null.
Any help is really appreciated.
Because your JSON has 2 different object types in the same array, you can't do typical and the easy way to deserialize into objects. Unless you have control over the JSON to change the structure into something that doesn't have the Inf and number object within same array, your only option is why Expando/dynamic objects. The code below parses the provided JSON into your existing class structure.
static List<JSONResponse> Parse(string json)
{
var responses = new List<JSONResponse>();
//string)obj[0][0].programCode.Value;
dynamic obj = JsonConvert.DeserializeObject<dynamic>(json);
for (int i = 0; i < obj.Count; i++)
{
//responses[i] = new JSONResponse() {
var inf = new List<Inf>();
var numbers = new List<number>();
for (int j = 0; j < obj[i].Count; j++)
{
if (obj[i][j].campaignId != null)
inf.Add(new Inf()
{
campaignId = (int) obj[i][j].campaignId.Value,
programCode = obj[i][j].programCode.Value
});
if (obj[i][j].reason != null)
numbers.Add(new number()
{
about = obj[i][j].about.Value,
reason = (int)obj[i][j].reason.Value
});
}
responses.Add(new JSONResponse()
{
number = numbers.ToArray(),
inf = inf.ToArray()
});
}
return responses;
}
Your JSON doesn't have the structure you show in your classes. Rather than a list of lists of responses containing arrays of Inf and number classes, you json has a list of pairs of Inf and number classes. You can see this if you paste your json into http://jsonformatter.curiousconcept.com/. It would make sense to package each pair into a container class, but unfortunately the pairs are transmitted in an array rather than as distinct properties, so they don't have names that could be mapped to class property names. Instead, you can write a custom JsonConverter to parse the array and assign the fields appropriately:
public class number
{
public int reason { get; set; }
public string about { get; set; }
}
public class Inf
{
public int campaignId { get; set; }
public string programCode { get; set; }
}
public class JSONResponse
{
public number number { get; set; }
public Inf Inf { get; set; }
public static List<JSONResponse> DeserializeList(string jsonstr)
{
return JsonConvert.DeserializeObject<List<JSONResponse>>(jsonstr, new JSONResponseConverter());
}
}
class JSONResponseConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(JSONResponse).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue, JsonSerializer serializer)
{
JSONResponse response = new JSONResponse();
var array = JArray.Load(reader);
if (array.Count != 2)
{
// Or maybe throw an exception?
Debug.WriteLine("Unexpected array length for " + array.ToString());
}
foreach (var entry in array)
{
if (entry["campaignId"] != null)
{
response.Inf = entry.ToObject<Inf>();
}
else if (entry["reason"] != null)
{
response.number = entry.ToObject<number>();
}
else
{
// Or maybe throw an exception?
Debug.WriteLine("Unknown entry " + entry.ToString());
}
}
return response;
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Assuming you are in charge of your data then rather than make things complex you could change your structure
eg instead of
#"[[{""campaignId"":201410018,""programCode"":""54321""},{""reason"":201410018,""about"":""54321""}],[{""campaignId"":201410019,""programCode"":""54322""},{""reason"":201410018,""about"":""54321""}]]"
try
#"[{info:{""campaignId"":201410018,""programCode"":""54321""},number:{""reason"":201410018,""about"":""54321""}},{info:{""campaignId"":201410019,""programCode"":""54322""},number:{""reason"":201410018,""about"":""54321""}}]"
and to handle that
public class JSONResponse
{
public number number{ get; set; }
public Inf info{ get; set; }
}
public class number
{
public int reason { get; set; }
public string about { get; set; }
}
public class Inf
{
public int campaignId { get; set; }
public string programCode { get; set; }
}
now you only have a list of JSONResponse to deal with
I am getting error, when i try to convert object into json using json.net.
Error:
Unable to cast object of type 'System.Collections.Generic.List`1[System.Int32]' to type
'MyNamespace.Domain.Entity'.
Class to be serialized:
[Serializable]
public class Business:Entity
{
public virtual string TemplateName { get; set; }
public virtual CalculationBasis CalculationBasis { get; set; }
public virtual PeriodSelectionType PeriodSelectionType { get; set; }
public virtual DateTime PeriodEndDate { get; set; }
public virtual IEnumerable<int> mainKeys { get; set; }
}
Serialization Code:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.NullValueHandling = NullValueHandling.Ignore;
settings.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
var strJson = JsonConvert.SerializeObject( ObjectOfBusiness, settings);
Deserialization Code:
JsonConvert.DeserializeObject<Business>(ObjectOfBusiness, settings);
I am only getting this error, when i have values in IEnumerable<int> mainKeys
NOTE: mainKeys is a List<int>
Looks like error is because of its parent class "Entity" , the class is like this:
[Serializable]
public abstract class Entity
{
public Entity()
{
}
public Entity(int id)
{
this.Id = id;
}
public virtual int Id { get; set; }
public override bool Equals(object obj)
{
Entity other = (Entity)obj;
return this.Id == other.Id;
}
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
}
Don't know why json.net is trying to convert IEnumerable into "entity" type (its parent class)..
I can't remove entity (parent class) as its been used from lots of places..
Please suggest.
Thanks
Finally found the solution.
the issue is because of poor code in override bool Equals(object obj) method.
The correct code is:
public override bool Equals(object obj)
{
if (obj is Entity)
{
Entity other = (Entity) obj;
return this.Id == other.Id;
}
else
{
return false;
}
}
http://json.codeplex.com/workitem/16554
This isn't really an answer - it is a "this works fine":
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
public class Entity { }
public class CalculationBasis { }
public class PeriodSelectionType { }
[Serializable]
public class Business : Entity
{
public virtual string TemplateName { get; set; }
public virtual CalculationBasis CalculationBasis { get; set; }
public virtual PeriodSelectionType PeriodSelectionType { get; set; }
public virtual DateTime PeriodEndDate { get; set; }
public virtual IEnumerable<int> mainKeys { get; set; }
}
class program
{
static void Main()
{
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.NullValueHandling = NullValueHandling.Ignore;
settings.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
var ObjectOfBusiness = new Business
{
TemplateName = "abc",
CalculationBasis = new CalculationBasis(),
PeriodSelectionType = new PeriodSelectionType(),
PeriodEndDate = new DateTime(),
mainKeys = new int[] { 1, 2, 3, 4, 5 }
};
var strJson = JsonConvert.SerializeObject(ObjectOfBusiness, settings);
//...
var obj = JsonConvert.DeserializeObject<Business>(strJson, settings);
// ^^^^ all good
}
}
So: it would be really good if you could show a failing case, or give us more clues to go on. For info, the JSON in the above is:
{"TemplateName":"abc","CalculationBasis":{},"PeriodSelectionType":{},"PeriodEndDate":"\/Date(-62135596800000)\/","mainKeys":[1,2,3,4,5]}