Euridice01 Euridice01 - 2 months ago 12
JSON Question

Converting JSON object to Data.Entities.Models.MyModel

So,

I'm having an issue with serializing my JSON object to my Entity/Model object.

I have the following code snippet:

var serializedValue = JsonConvert.SerializeObject(newRows, new MyModelConverter());

var csvToSave = JsonConvert.DeserializeObject<MyModel>(serializedValue);


Where newRows is a
List<KeyValuePair<string,string>>


Where Entity looks something like this:

public class MyModel: Entity
{
public int Id { get; set; } //primary key

public DateTime TouchTime { get; set; }

public DateTime InstallTime { get; set; }

public string EventName { get; set; }

}


Looking back at old StackOverflow posts, I see I need to use a custom JsonConverter so that I can get my data to be converted to a json object.

Here's my converter class:

public class MyModelConverter : JsonCreationConverter<MyModel>
{
protected override MyModel Create(Type objectType, JObject jObject)
{
return new MyModel();
}
}

public abstract class JsonCreationConverter<T> : JsonConverter
{
/// <summary>
/// Create an instance of objectType, based properties in the JSON object
/// </summary>
/// <param name="objectType">type of object expected</param>
/// <param name="jObject">
/// contents of JSON object that will be deserialized
/// </param>
/// <returns></returns>
protected abstract T Create(Type objectType, JObject jObject);

public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}

public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);

// Create target object based on JObject
T target = Create(objectType, jObject);

// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);

return target;
}

public override void WriteJson(JsonWriter writer,
object value,
JsonSerializer serializer)
{
List<KeyValuePair<string, object>> list = value as List<KeyValuePair<string, object>>;
writer.WriteStartArray();
foreach (var item in list)
{
writer.WriteStartObject();
writer.WritePropertyName(item.Key);
writer.WriteValue(item.Value);
writer.WriteEndObject();
}
writer.WriteEndArray();
}
}


But when I run my program, I get this error instead:

Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Data.Entities.Models.MyModel' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.


Where my Json object looks like this now:

[
{
"Key":"Attributed Touch Time",
"Value":"2016-09-06 20:11:50"
},
{
"Key":"Install Time",
"Value":"2016-09-06 20:14:12"
},
{
"Key":"Event Name",
"Value":"install"
}
]


Then I fixed my converter like so:

public abstract class JsonCreationConverter<T> : JsonConverter
{
/// <summary>
/// Create an instance of objectType, based properties in the JSON object
/// </summary>
/// <param name="objectType">type of object expected</param>
/// <param name="jObject">
/// contents of JSON object that will be deserialized
/// </param>
/// <returns></returns>
protected abstract T Create(Type objectType, JObject jObject);

public override bool CanConvert(Type objectType)
{
return objectType == typeof(List<KeyValuePair<string, string>>);
}

public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);

// Create target object based on JObject
T target = Create(objectType, jObject);

// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);

return target;
}

public override void WriteJson(JsonWriter writer,
object value,
JsonSerializer serializer)
{
List<KeyValuePair<string, string>> list = value as List<KeyValuePair<string, string>>;
writer.WriteStartArray();
foreach (var item in list)
{
writer.WriteStartObject();
writer.WritePropertyName(item.Key);
writer.WriteValue(item.Value);
writer.WriteEndObject();
}
writer.WriteEndArray();
}
}


But my Json object now looks like this:

[
{
"Attributed Touch Time":"2016-09-06 20:11:50"
},
{
"Install Time":"2016-09-06 20:14:12"
},
{
"Event Name":"install"
},
(etc...) //Not part of JSON, just cutting it short
]


Which is great but still won't map to my entities

Answer

It looks like you're creating a new JSON object for each KVP rather than grouping the KVP's into a single object. Move the writer.WriteStartObject(); and writer.EndObject(); to outside of the loop and you should end up with a nicely formed JSON Object.

   writer.WriteStartObject();

   foreach (var item in list)
    {

        writer.WritePropertyName(item.Key);
        writer.WriteValue(item.Value);

    }

    writer.WriteEndObject();
    writer.WriteEndArray();

You need to take those Start/End Objects to outside the For loop.

So you should get this instead...

[  
   {  
      "Attributed Touch Time":"2016-09-06 20:11:50"
      "Install Time":"2016-09-06 20:14:12"
      "Event Name":"install"
   },
]