Same question as Deserializing stringified (quote enclosed) nested objects with Jackson but for C# and Newtonsoft Json.Net. The question is how to deserialize a nested string representation of JSON using Json.Net. Example response from REST endpoint:
{
"Id": "abcd1234",
"Foo": "{\"field1\": "abc", \"field2\": \"def\"}"
}
C# Model:
public class Model {
public Guid Id { get; set; }
public Dictionary<string, string> Foo { get; set; }
}
Just calling JsonConvert.DeserializeObject<Model>(returnedJson) will error out because Foo in its returned form is stringified JSON, aka just a string. This is the case for other fields in our JSON response as well that have different Dictionary types associated in their models (e.g. Dictionary<string, int> and Dictionary<string, DateTime>).
What's the simplest way to handle this? Is a custom JsonConverter necessary or is there some built-in way to handle it?
A JsonConverter is probably the simplest way to handle this implicit conversion. Here's one that works for your case.
public class MyConverter: JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JsonConvert.DeserializeObject(reader.Value as string, objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then you decorate that property with the attribute like this:
public class Model
{
public string Id { get; set; }
[JsonConverter(typeof(MyConverter))]
public Dictionary<string, string> Foo { get; set; }
}
Just to follow up, I used a very similar solution to what #VijayeR recommended, but needed to tweak it a bit as it was still causing some problems for me. One issue is that when using the decorator, apparently CanConvert doesn't get called at all because Json.Net assumes you've assigned the correct converter to that property. This is what worked for me:
public class NestedJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jToken = JToken.Load(reader);
if (jToken.Type == JTokenType.String)
{
jToken = JToken.Parse((string) jToken);
}
return jToken.ToObject(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanWrite => false;
}
Related
Same question as Deserializing stringified (quote enclosed) nested objects with Jackson but for C# and Newtonsoft Json.Net. The question is how to deserialize a nested string representation of JSON using Json.Net. Example response from REST endpoint:
{
"Id": "abcd1234",
"Foo": "{\"field1\": "abc", \"field2\": \"def\"}"
}
C# Model:
public class Model {
public Guid Id { get; set; }
public Dictionary<string, string> Foo { get; set; }
}
Just calling JsonConvert.DeserializeObject<Model>(returnedJson) will error out because Foo in its returned form is stringified JSON, aka just a string. This is the case for other fields in our JSON response as well that have different Dictionary types associated in their models (e.g. Dictionary<string, int> and Dictionary<string, DateTime>).
What's the simplest way to handle this? Is a custom JsonConverter necessary or is there some built-in way to handle it?
A JsonConverter is probably the simplest way to handle this implicit conversion. Here's one that works for your case.
public class MyConverter: JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JsonConvert.DeserializeObject(reader.Value as string, objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then you decorate that property with the attribute like this:
public class Model
{
public string Id { get; set; }
[JsonConverter(typeof(MyConverter))]
public Dictionary<string, string> Foo { get; set; }
}
Just to follow up, I used a very similar solution to what #VijayeR recommended, but needed to tweak it a bit as it was still causing some problems for me. One issue is that when using the decorator, apparently CanConvert doesn't get called at all because Json.Net assumes you've assigned the correct converter to that property. This is what worked for me:
public class NestedJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jToken = JToken.Load(reader);
if (jToken.Type == JTokenType.String)
{
jToken = JToken.Parse((string) jToken);
}
return jToken.ToObject(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanWrite => false;
}
I'm querying a service which returns a list of tags in multiple formats:
{
"tags": "a,b,c"
}
or
{
"tags": "a b c"
}
or
{
"tags": "a+b+c"
}
The object that I want to deserialize this to is a List<string>. I've written a TagsConverter that implements the necessary methods in JsonConverter like so:
public class TagsConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(string));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return reader
.Value
.ToString()
.Split(' ')
.ToList();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var list = (List<string>)value;
var delimitedList = string.Join(" ", list);
writer.WriteValue(delimitedList);
}
}
Then I annotate the field in the object like so:
public class Foo
{
[JsonConverter(typeof(TagsConverter))]
public List<string> Tags { get; set; }
}
And this works, but only for tags that are separated by spaces.
However the TagsConverter can work in all 3 scenarios if I'm able to just change the Split and the Join functions in a parameterized fashion. But since we pass only the type to the JsonConverter attribute, I understand that we can't pass a "delimiter" as an argument there.
Is there any way to achieve this?
There is an additional JsonConverter constructor overload that takes both a type and a params object[] converterParameters that is injected into the concrete instance of your converter. You can take advantage of this overload to maximize your reusability.
public class TagsConverter : JsonConverter
{
private readonly string _delimiter;
public TagsConverter(string delimiter)
{
_delimiter = delimiter;
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(string));
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
return reader
.Value
.ToString()
.Split(_delimiter.ToCharArray())
.ToList();
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
var list = (List<string>)value;
var delimitedList = string.Join(_delimiter, list);
writer.WriteValue(delimitedList);
}
}
Utilizing the overload is as easy as passing it as a second parameter:
public class Foo
{
[JsonConverter(typeof(TagsConverter), " ")]
public List<string> Tags { get; set; }
}
If only needed for the reading/deserialization of the JSON do note that string.Split can take a character array char[]
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return reader
.Value
.ToString()
.Split(new char[]{' ', ',', '+'}, StringSplitOptions.RemoveEmptyEntries)
.ToList();
}
Which means that the converter can handle all three delimiters when reading.
I'd like to have property of type object that can be either a string or Template type.
Is it possible to tell Json.NET to parse something into one of several specified types?
class MyClass
{
public object Template { get; set; }
}
where Template = "TemplateName"
{
"Template": "TemplateName"
}
or Template = new Template()
{
"Template": { "Name": "OtherTamplate", ... }
}
UPDATE:
I tried to follow #krillgar' advice and create a custom JsonConverter but unfortunatelly the CanConvert method receives only the target type, in this case object. This information is not enough to tell wheter it can be deserialized (in case I had other object properties). I guess I need it to be a Template after all or create a derived type like TemplateReference or something:
class myconverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
// objectType = typeof(object)
throw new NotImplementedException();
}
}
Configuration = JsonConvert.DeserializeObject<MyClass>(text, new myconverter());
Disclaimer
This question has once been closed as a duplicate of How to deserialize a JSON property that can be two different data types using Json.NET. Because at the time of writing my question I hadn't known that there already was a similar one I'd like to clarify the difference between them to prevent it from being closed in future:
The other question is about how to deserialize different values into a concrete type whereas mine is about deserializing different values into an object. It might seem to be the same at the first look because in both examples only the type of the property is different but it has a huge impact on the overall application design. It's important for me that I can use an object to store different specialized types rather then one type having multiple responsibilities.
This problem can be solved by using a custom JsonConverter. Here is a generic version that should work for this situation:
class ObjectOrStringConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// CanConvert is not called when the [JsonConverter] attribute is used
return false;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Object)
{
return token.ToObject<T>(serializer);
}
return token.ToString();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
To use the converter, all you need to do is add a [JsonConverter] attribute to the property in your class that can be either a string or an object. The generic type parameter must match the type of non-string object you are expecting.
class MyClass
{
[JsonConverter(typeof(ObjectOrStringConverter<Template>))]
public object Template { get; set; }
}
Below is a demonstration of the converter in action:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("--- first run ---");
string json = #"
{
""Template"": ""TemplateName""
}";
DeserializeAndDump(json);
Console.WriteLine("--- second run ---");
json = #"
{
""Template"": { ""Name"": ""OtherTemplate"" }
}";
DeserializeAndDump(json);
}
static void DeserializeAndDump(string json)
{
MyClass obj = JsonConvert.DeserializeObject<MyClass>(json);
if (obj.Template == null)
{
Console.WriteLine("Template property is null");
}
else
{
Console.WriteLine("Template property is a " + obj.Template.GetType().Name);
string name = "(unknown)";
if (obj.Template is Template) name = ((Template)obj.Template).Name;
else if (obj.Template is string) name = (string)obj.Template;
Console.WriteLine("Template name is \"" + name + "\"");
}
Console.WriteLine();
}
}
class Template
{
public string Name { get; set; }
}
And here is the output from the above:
--- first run ---
Template property is a String
Template name is "TemplateName"
--- second run ---
Template property is a Template
Template name is "OtherTemplate"
Fiddle: https://dotnetfiddle.net/Lw3RaN
I don't know if you can do that, but you could go another way. Change your "Template" property to be a Template instead of an object and use a custom property of the Template class to know weither you want to serialize it as a Template or a string.
class MyClass
{
[JsonConverter(typeof(TemplateConverter))]
public Template Template { get; set; }
}
class Template
{
/* Your Template class */
public Type TypeToSerializeInto { get; private set; }
}
public class TemplateConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Template);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Template val = value as Template;
writer.WriteStartObject();
writer.WritePropertyName("Template");
if (val.TypeToSerializeInto == typeof(Template))
serializer.Serialize(writer, val);
else if (val.TypeToSerializeInto == typeof(string))
serializer.Serialize(writer, val.Name);
writer.WriteEndObject();
}
}
If you have a property, which type is an abstract type - like object, on de-serialization, you can know the specific type that was serialized by serializing somewhere also the name of that specific type.
So your json should look like this:
{
"MyClass": {
"Template": "some name",
"type": "System.String"
}
}
This way on deserialization you can check what type was that property before the serialization (in this case String)
Another way to determine the type is by checking the json structure as you can see here:
C#: Deserializing JSON when one field can be different types
Why my custom JsonConverter.WriteJson() method doesn't get called ?
class MyType{
[JsonConverter(typeof(DocumentXamlDeserializer))]
public string GuiData { get; set; }
public string SimpleString;
}
Although the ReadJson does get called:
public class DocumentXamlDeserializer : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(Gui.Handler.SerializeData());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var xaml = reader.Value as string;
Gui.Handler.DeserializeData(xaml);
return xaml;
}
public override bool CanConvert(Type objectType) { return typeof(string).IsAssignableFrom(objectType); }
}
The serialization call: JsonConvert.SerializeObject(dataModel, Formatting.Indented);
The deserialization call: JsonConvert.DeserializeObject<Model>(raw);
Apparently this is because GuiData is null... I guess I could specify:
TypeNameHandling = TypeNameHandling.Objects
But I want to serialize only GuiData even if it's null (I set its value during serialization), without serializing all null-properties... well, if I don't find better way, I guess I'd have to suffice with it...
I notice there are some other results on stackoverflow for this question but they don't seem to work or are vague. Using the most popular result, I have put together this:
The problem is that when JSON comes back and is being serialised into one of my custom types, one of the bits of JSON is sometimes an array, and sometimes just a string. If my custom type has a string, and the JSON is an array, I get an error. The same happens the other way around, if the JSON is an object and my custom type is an array. It just can't map it to the property.
I decided to solve this, I want to override the deserialisation of this particular property, and if it's an object, I want to convert it into an array of 1 object.
In the object I am serialising to, I added a JsonConverter which I think is going to override the way it's deserialised.
[JsonConverter(typeof(ArrayOrSingleObjectConverter<string>))]
public List<string> person { get; set; }
The idea is that the custom converter will convert a single object to an array. So if the JSON is "Hello" it will set person to be a List containing "Hello" instead of throwing an exception saying cannot convert string to List.
If it's already an array, it should just leave it alone.
The converter looks like this:
public class ArrayOrSingleObjectConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true; // Not sure about this but technically it can accept an array or an object, so everything is game.
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (objectType == typeof(List<T>))
{
return serializer.Deserialize<List<T>>(reader);
}
else
{
var singleObject = serializer.Deserialize<T>(reader);
var objectAsList = new List<T>();
objectAsList.Add(singleObject);
return objectAsList;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
It doesn't work. The above code throws an exception trying to deserialize a a single string saying it can't cast it into a List inside the if statement (the 'objectype' is however a List).
I'm struggling to understand exactly what the read and write methods are doing. In the other answer on stackoverflow, it suggests throwing a NotImplementedException in the read method. But if I do that, the read method is called and the exception throws.
I think I'm on the right track, but I need a nudge in the right direction. I think I'm a little confused about what the ReadJSon method is doing and what its parameters mean.
I don't really understand where the value is coming from that it's deserializing since I didn't specify it in the Deserialize method call.
I'm a bit out of my depth on this one.
I had to do something similar last week and I came up with the following, which works fine for a List rather than an array
internal class GenericListCreationJsonConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize<List<T>>(reader);
}
else
{
T t = serializer.Deserialize<T>(reader);
return new List<T>(new[] { t });
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
I like this method which makes Json.NET do all the heavy lifting. And as a result, it supports anything that Json.NET supports (List<>, ArrayList, strongly-typed arrays, etc).
public class FlexibleCollectionConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize(reader, objectType);
}
var array = new JArray(JToken.ReadFrom(reader));
return array.ToObject(objectType);
}
public override bool CanConvert(Type objectType)
{
return typeof (IEnumerable).IsAssignableFrom(objectType);
}
}