CustomConverter doesn't works to get names of objects - c#

I receive a json like this :
{
"Bryan": {
"age": 25,
"city": "Miami"
},
"Jeff": {
"age": 24,
"city": "Tokyo"
}
}
I would like deserialize to get the name of the persons in a list<String>.
So I have a CustomJsonConverter:
public class JsonObjectsToListConverter : JsonConverter
{
public JsonObjectsToListConverter()
{
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(HashSet<String>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken jtoken = JToken.Load(reader);
JObject jObjectCast = jtoken.Value<JObject>();
List<String> listPers = (from prop in jObjectCast.Properties()
select prop.Name).ToList();
return listPers;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
and a Person class with List member :
public class Persons
{
[JsonConverter(typeof(JsonObjectsToListConverter))]
public List<String> listPers { get; set; }
}
The problem is that the CustomConverter return null except if I add a parent object into json like :
{"listP":
{
"Bryan":{
....
and [JsonProperty("listP")] in Persons Class.
I don't understand what happens during deserialization and why it doesn't works properly without PropertyName Attribute.

The description below is very simplified, but pretty close to the actual process of deserialization.
Once you supply the JsonConvert.DeserializeObject method with a string and the target type, it starts to compare the structure of the type you specified with the structure of the string. It looks for the public properties of the supplied types and searches the string for the same property occurrences. It doesn't know that the json array you specify in string should go into the property named "listP" until you link between json and the type using name for the json array.
Now, you don't necessary have to specify the "listP" property name with the JsonProperty attribute. It's enough to just the json array the same name the class property has: "listPers".
If you'd like to simplify the process of deserialization, however, without additional overhead in the json itself and unnecessary properties decoration, it would be easier to write a code similar to the below:
public class Persons
{
public List<String> ListPers { get; set; }
}
public class JsonObjectsToPersonsConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Persons));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
Persons value = new Persons();
JToken jtoken = JToken.Load(reader);
JObject jObjectCast = jtoken.Value<JObject>();
List<String> listPers = (from prop in jObjectCast.Properties()
select prop.Name).ToList();
return new Persons { ListPers = listPers};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And deserialize the data like this:
var persons = JsonConvert.DeserializeObject<Persons>(
#"{
'Bryan': {
'age': 25,
'city': 'Miami'
},
'Jeff': {
'age': 24,
'city': 'Tokyo'
}
}",
new JsonObjectsToPersonsConverter());

Related

Newtonsoft Json serialize a class where one of the properties is JSON [duplicate]

In order to increase performance, I have cached the result of a larger operation as JSON in a table - together with a key column to determine which row(s) to return. So the data looks some like this:
Id Json
---- ---------
1 {"property": "data", "...": "..."}
2 {"property": "data", "...": "..."}
Hence, my retrieved object has the properties int .Id and string .Json. When returning such an object with the Id, I first need to deserialize the JSON - so that it gets properly re-serialized. If I don't deserialize it first, I end up with a quoted string, i.e. my return object would look like this
{
"id": 1,
"json": "{\"property\": \"data\", ...
}
Instead, I need:
{
"id": 1,
"json": {
"property": "data",
...
}
}
Is there a way to "tell" the Json.Net serializer to output the .Json property directly without serializing - while serializing the other properties?
You could make a JsonConverter to write the raw value of the string property to the output without changing it. You take responsibility for ensuring the string has valid JSON or else the resulting output will not be valid JSON either.
Here is what the converter might look like:
class RawJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(string));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// write value directly to output; assumes string is already JSON
writer.WriteRawValue((string)value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// convert parsed JSON back to string
return JToken.Load(reader).ToString(Formatting.None);
}
}
To use it, mark your JSON property with a [JsonConverter] attribute like this:
class Foo
{
...
[JsonConverter(typeof(RawJsonConverter))]
public string YourJsonProperty { get; set; }
...
}
Here is a demo: https://dotnetfiddle.net/BsTLO8
Assuming you have a structure like this for serializing:
public class Record
{
[JsonProperty("id")]
public int Id
{
get;
set;
}
[JsonProperty("json")]
[JsonConverter(typeof(SpecialJsonConverter))]
public string Json
{
get;
set;
}
}
And you use code like this for serialization:
var data = new []
{
new Record() { Id=1, Json = "{\"property\":\"data\"}" },
new Record() { Id=2, Json = "{\"property\":\"data2\", \"property2\":[1, 2, 3]}" }
};
var serialized = JsonConvert.SerializeObject(data);
Console.WriteLine(serialized);
All you need is to write a proper converter for the Json property. Luckily there is a method WriteToken in the JsonWriter class that could serve our needs:
public sealed class SpecialJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var reader = new JsonTextReader(new StringReader(value.ToString()));
writer.WriteToken(reader);
}
}
Based on answer by Alex and comment by Shahin, I improved the converter a bit, and also implemented the reader to work also the other way (parse back from JToken to the string property):
public sealed class RawDataJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var tokenReader = reader as JTokenReader;
var data = tokenReader.CurrentToken.ToString(Formatting.None);
return data;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteToken(JsonToken.Raw, value);
}
}

Serialize object to JSON that already contains one JSON property

In order to increase performance, I have cached the result of a larger operation as JSON in a table - together with a key column to determine which row(s) to return. So the data looks some like this:
Id Json
---- ---------
1 {"property": "data", "...": "..."}
2 {"property": "data", "...": "..."}
Hence, my retrieved object has the properties int .Id and string .Json. When returning such an object with the Id, I first need to deserialize the JSON - so that it gets properly re-serialized. If I don't deserialize it first, I end up with a quoted string, i.e. my return object would look like this
{
"id": 1,
"json": "{\"property\": \"data\", ...
}
Instead, I need:
{
"id": 1,
"json": {
"property": "data",
...
}
}
Is there a way to "tell" the Json.Net serializer to output the .Json property directly without serializing - while serializing the other properties?
You could make a JsonConverter to write the raw value of the string property to the output without changing it. You take responsibility for ensuring the string has valid JSON or else the resulting output will not be valid JSON either.
Here is what the converter might look like:
class RawJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(string));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// write value directly to output; assumes string is already JSON
writer.WriteRawValue((string)value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// convert parsed JSON back to string
return JToken.Load(reader).ToString(Formatting.None);
}
}
To use it, mark your JSON property with a [JsonConverter] attribute like this:
class Foo
{
...
[JsonConverter(typeof(RawJsonConverter))]
public string YourJsonProperty { get; set; }
...
}
Here is a demo: https://dotnetfiddle.net/BsTLO8
Assuming you have a structure like this for serializing:
public class Record
{
[JsonProperty("id")]
public int Id
{
get;
set;
}
[JsonProperty("json")]
[JsonConverter(typeof(SpecialJsonConverter))]
public string Json
{
get;
set;
}
}
And you use code like this for serialization:
var data = new []
{
new Record() { Id=1, Json = "{\"property\":\"data\"}" },
new Record() { Id=2, Json = "{\"property\":\"data2\", \"property2\":[1, 2, 3]}" }
};
var serialized = JsonConvert.SerializeObject(data);
Console.WriteLine(serialized);
All you need is to write a proper converter for the Json property. Luckily there is a method WriteToken in the JsonWriter class that could serve our needs:
public sealed class SpecialJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var reader = new JsonTextReader(new StringReader(value.ToString()));
writer.WriteToken(reader);
}
}
Based on answer by Alex and comment by Shahin, I improved the converter a bit, and also implemented the reader to work also the other way (parse back from JToken to the string property):
public sealed class RawDataJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var tokenReader = reader as JTokenReader;
var data = tokenReader.CurrentToken.ToString(Formatting.None);
return data;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteToken(JsonToken.Raw, value);
}
}

Merge two objects during serialization using json.net?

I met a situation as below could anybody help me achieve as below?
For Example, if I have the class:-
public class Sample
{
public String name {get;set;}
public MyClass myclass {get;set;}
}
My Myclass will be as follow:
public class MyClass
{
public String p1 {get;set;}
public String p2 {get;set;}
}
When I am using Json.net to Serialize the object of the class Sample,I got as below and it works well.
{
"name":"...",
"myclass":
{
"p1":"...",
"p2":"..."
}
}
Its correct but I wonder is it possible to get the json string as below?
{
"name":"...",
"p1":"...",
"p2":"..."
}
You can create anonymous object and serialize it:
var sample = new Sample {
name = "Bob",
myclass = new MyClass {
p1 = "x",
p2 = "y"
}};
string json = JsonConvert.SerializeObject(new {
sample.name,
sample.myclass.p1,
sample.myclass.p2
});
Result
{"name":"Bob","p1":"x","p2":"y"}
But I suggest you either use default serialization of your Sample class, or create class which will be serialized into your format (i.e. move MyClass properties into Sample).
UPDATE: You can use custom converter, which flattens object and serializes all inner objects properties as top level object properties:
public class FlattenJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
return;
}
JObject o = (JObject)t;
writer.WriteStartObject();
WriteJson(writer, o);
writer.WriteEndObject();
}
private void WriteJson(JsonWriter writer, JObject value)
{
foreach (var p in value.Properties())
{
if (p.Value is JObject)
WriteJson(writer, (JObject)p.Value);
else
p.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return true; // works for any type
}
}
Usage:
string json = JsonConvert.SerializeObject(sample, new FlattenJsonConverter());
Or you can simply hide anonymous type creation into custom converter, if you need this behavior for one type only:
public class SampleJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
Sample sample = (Sample)value;
JToken t = JToken.FromObject(new {
sample.name,
sample.myclass.p1,
sample.myclass.p2
});
t.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Sample);
}
}

Object cannot by deserialized, because 'Collection was of a fixed size.'

I have easy example of my real code. I need serialize to JSON and deserialize back object of class TestClass, which is derived from Letters. Both classes have constructor with parameter.
public class TestClass : Letters
{
public string[] Names { get; private set; }
public TestClass(string[] names)
: base(names)
// : base(new [] { "A", "B", })
// : base(names.Select(a => a.Substring(0, 1)).ToArray())
{
Names = names;
}
}
public abstract class Letters
{
public string[] FirstLetters { get; private set; }
protected Letters(string[] letters)
{
FirstLetters = letters;
}
}
Object of TestClass is serialized to valid JSON, but when I try it deserialize back to object, NotSupportedException is throw with message Collection was of a fixed size.
Here is my test
[Fact]
public void JsonNamesTest()
{
var expected = new TestClass(new [] { "Alex", "Peter", "John", });
var serialized = JsonConvert.SerializeObject(expected);
Console.WriteLine(serialized);
Assert.False(string.IsNullOrWhiteSpace(serialized));
var actual = JsonConvert.DeserializeObject<TestClass>(serialized);
AssertEx.PrimitivePropertiesEqual(expected, actual);
}
Json.Net needs all classes to have a parameterless constructor in order to deserialize them, otherwise it doesn't know how to call the constructor. One way to get around this without changing your classes is to make a custom JsonConverter that will create the object instance from the JSON. For example:
class TestClassConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(TestClass) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
string[] names = jo["Names"].ToObject<string[]>();
return new TestClass(names);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
Then, deserialize your class like this and it should work:
var actual = JsonConvert.DeserializeObject<TestClass>(serialized, new TestClassConverter());
Thank, it works! I modified your code for more general usage in my example.
I suppose
is only one public constructor
serialized parameters have same name as constructor parameters (ignore case)
public class ParametersContructorConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Letters).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jo = JObject.Load(reader);
var contructor = objectType.GetConstructors().FirstOrDefault();
if (contructor == null)
{
return serializer.Deserialize(reader);
}
var parameters = contructor.GetParameters();
var values = parameters.Select(p => jo.GetValue(p.Name, StringComparison.InvariantCultureIgnoreCase).ToObject(p.ParameterType)).ToArray();
return contructor.Invoke(values);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}

C# JSON custom serialization

Is there a way/library that will allow me to customize JSON serialization similar to GSON custom serializers?
Here is what I'm trying to get:
this object: KeyValuePair("Age",10) myAge
will normally get serialized like
"myAge": {
"Key": "Age",
"Value": 10
}
whilst I want it to serialize like: "Age": 10 instead. Any ideas?
First i suggest to use newton json dll.
Second your need create custom JsonConverter for KeyValuePair like this:
public class PairConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(KeyValuePair<string, int>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var item = (KeyValuePair<string, int>)value;
writer.WriteValue(item.Value);
writer.Flush();
}
}
And than add JsonConverter attribute for 'Age' property like this:
public class Persons
{
[JsonConverter(typeof(PairConverter))]
public KeyValuePair<string, int> Age { get; set; }
}
Finally, example of usage:
var persons = new Persons()
{
Age = new KeyValuePair<string, int>("Age", 10)
};
var json = JsonConvert.SerializeObject(persons); // {Age:10}

Categories