suryakiran
suryakiran

Reputation: 1996

C# JSON String Deserialization using JSON.Net

I would have my JSON string as below which needs to be deserialized to the below class

{
    "Id":"1",
    "Names":{
        "string":"Surya"
    },
    "ClassTypes":{
        "Types":[
            {
                "TypeId":"1",
                "TypeName":"First Name"
            },
            {
                "TypeId":"2",
                "TypeName":"Last Name"
            }
        ]
    },
    "Status":"1",
    "DOB":"23/12/2014"
}

Class Definition

public class MyClass
{
    public int Id { get; set; }
    public IList<string> Names { get; protected set; }
    public IList<Types> ClassTypes { get; protected set; }
    public StatusType Status { get; set; }
    public DateTime DOB { get; set; }

    public MyClass()
    {
        Names = new List<string>();
        ClassTypes = new List<Types>();
        Status = StatusType.Active;
    }
}
public class Types
{
    public int TypeId { get; set; }
    public string TypeName { get; set; }
}

public enum StatusType
{
    Active = 0,
    InActive = 1
}

I have used the following code for deserialization, but getting error

var serializerSettings = new JsonSerializerSettings();
serializerSettings.ContractResolver = new IncludeNonPublicMembersContractResolver();
JsonConvert.DeserializeObject<MyClass>(jsonString, serializerSettings);

public class IncludeNonPublicMembersContractResolver : DefaultContractResolver
{
    public IncludeNonPublicMembersContractResolver()
    {
        DefaultMembersSearchFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
    }
    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        var members = base.GetSerializableMembers(objectType);
        return members.Where(m => !m.Name.EndsWith("k__BackingField")).ToList();
    }

}

Error Details

Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.IList`1[System.String]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.

To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.

Path 'Names.string', line 1, position 28.

Note - I can't change by JSON string and solution to be more generic has this would be required for other classes as well :(

Upvotes: 2

Views: 504

Answers (2)

FoggyFinder
FoggyFinder

Reputation: 2220

One way is to use a JsonConverter:

  public class MyClass
    {
        public int Id { get; set; }
        [JsonProperty("Names"), JsonConverter(typeof(NamesConverter))]
        public IList<string> Names { get; protected set; }
        [JsonProperty("ClassTypes"), JsonConverter(typeof(TypesConverter))]
        public IList<Types> ClassTypes { get; protected set; }
        public StatusType Status { get; set; }
        [JsonProperty("DOB"), JsonConverter(typeof(DateTimeConverter))]
        public DateTime DOB { get; set; }

        public MyClass()
        {
            Names = new List<string>();
            ClassTypes = new List<Types>();
            Status = StatusType.Active;
        }

        public override string ToString()
        {
            return
                string.Format("ID: {0}\nNames:\n{1}\nClassTypes:\n{2}Status: {3}\nDOB: {4}",
                Id, string.Join("\n", Names), string.Join("\n", ClassTypes), Status, DOB);
        }
    }
    public class Types
    {
        public int TypeId { get; set; }
        public string TypeName { get; set; }

        public override string ToString()
        {
            return string.Format("\tTypeId: {0}\n\tTypeName: {1}\n", TypeId, TypeName);
        }
    }

    public enum StatusType
    {
        Active = 0,
        InActive = 1
    }

    class NamesConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(Dictionary<string, string>).IsAssignableFrom(objectType);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return serializer.Deserialize<Dictionary<string, string>>(reader).Values.ToList();
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

    }
    class TypesConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(Dictionary<string,List<Types>>).IsAssignableFrom(objectType);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return serializer.Deserialize<Dictionary<string,List<Types>>>(reader)
                .Values.First().ToList();
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

    }
    class DateTimeConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(string).IsAssignableFrom(objectType);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return DateTime.ParseExact(serializer.Deserialize<string>(reader), @"dd\/MM\/yyyy", null);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

    }

...

    var jsonobj = JsonConvert.DeserializeObject<MyClass>(json);
    Console.WriteLine(jsonobj);

Print:

Names:
Surya
ClassTypes:
    TypeId: 1
    TypeName: First Name

    TypeId: 2
    TypeName: Last Name
Status: InActive
DOB: 12/23/2014 12:00:00 AM

https://dotnetfiddle.net/EfQuET

Two way - parse json, but then you need to change the access for the properties Names,ClassTypes or add the constructor:

var jObject = JObject.Parse(json);
var names = new List<string> 
{ 
    jObject["Names"].Value<string>("string")
};
var types = jObject["ClassTypes"]["Types"]
    .Select(x => new Types
    {
        TypeId = x.Value<int>("TypeId"),
        TypeName = x.Value<string>("TypeName")
    })
        .ToList();
var data = new MyClass(names,types)
{
    Id = jObject.Value<int>("Id"),
    Status = (StatusType)Enum.Parse(typeof(StatusType),jObject.Value<string>("Status")),
    DOB = DateTime.ParseExact(jObject.Value<string>("DOB"), @"dd\/MM\/yyyy", null)
};

Console.WriteLine(data);

https://dotnetfiddle.net/HcPxT9

Upvotes: 2

Backs
Backs

Reputation: 24903

Names is not an array, it's an object:

public class Names
{
    public string @string { get; set; }
}

public class Type
{
    public string TypeId { get; set; }
    public string TypeName { get; set; }
}

public class ClassTypes
{
    public List<Type> Types { get; set; }
}

public class MyClass
{
    public int Id { get; set; }
    public Names Names { get; set; }
    public ClassTypes ClassTypes { get; set; }
    public string Status { get; set; }
    public string DOB { get; set; }
}
...

var result = JsonConvert.DeserializeObject<MyClass>(jsonString);

Classes created by http://json2csharp.com/

Upvotes: 2

Related Questions