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}
Related
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());
I'm using Json.Net to parse JSON into my app logic.
The problem is that the external API that I get the JSON from sometimes has "null" items inside their lists.
I would like to remove those "null" items from the list (or any other IEnumerable that might have that) at parse time.
I believe the solution has to be using a JsonConverter but I was unable to get it working so far.
MyData data = new MyData();
Newtonsoft.Json.JsonSerializerSettings settings = new Newtonsoft.Json.JsonSerializerSettings
{
Converters = new List<JsonConverter>() { new TrimNullListValues() }
};
string jsonString = #"{""ListData"": [{""source"" : 10 , ""target"" : 20, ""Data"" : [{""source"" : 100 , ""target"" : 200}, null]}, null]}";
JsonConvert.PopulateObject(jsonString, data, settings);
MyData class is like this:
public class MyData {
public class MyNestedData
{
public int Source;
public int Target;
public List<MyNestedData> Data;
}
public List<MyNestedData> ListData;
}
My JsonConverter (TrimNullListValues) is like this:
public class TrimNullListValues : 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)
{
// Don't really know what to do in here to remove unwanted values
// From the IEnumerabes
}
public override bool CanConvert(Type objectType)
{
return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(List<>);
}
}
You could try something like this:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
foreach (JToken item in array.ToList())
{
if (item.Type == JTokenType.Null)
item.Remove();
}
object list = Activator.CreateInstance(objectType);
serializer.Populate(array.CreateReader(), list);
return list;
}
Fiddle: https://dotnetfiddle.net/SESCfZ
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);
}
}
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);
}
}
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);
}
}