C# Json.Net Deserialize Json with different "key" parameter - c#

I am trying to deserialize JSON using the Json.NET library. JSON which I receive looks like:
{
"responseHeader": {
"zkConnected": true,
"status": 0,
"QTime": 2
},
"suggest": {
"mySuggester": {
"Ext": {
"numFound": 10,
"suggestions": [
{
"term": "Extra Community",
"weight": 127,
"payload": ""
},
{
"term": "External Video block",
"weight": 40,
"payload": ""
},
{
"term": "Migrate Extra",
"weight": 9,
"payload": ""
}
]
}
}
}
}
The problem is that the "Ext" that you can see in it is part of the parameter passed in the query string and will always be different. I want to get only the values assigned to the term "term".
I tried something like this, but unfortunately does not works:
public class AutocompleteResultsInfo
{
public AutocompleteResultsInfo()
{
this.Suggest = new Suggest();
}
[JsonProperty("suggest")]
public Suggest Suggest { get; set; }
}
public class Suggest
{
[JsonProperty("mySuggester")]
public MySuggesterElement MySuggesterElement { get; set; }
}
public struct MySuggesterElement
{
public MySuggester MySuggester;
public string JsonString;
public static implicit operator MySuggesterElement(MySuggester MySuggester) =>new MySuggesterElement { MySuggester = MySuggester };
public static implicit operator MySuggesterElement(string String) => new MySuggesterElement { JsonString = String };
}
public class MySuggester
{
[JsonProperty("suggestions")]
public Suggestions[] Suggestions { get; set; }
}
public class Suggestions
{
[JsonProperty("term")]
public string Autocopmplete { get; set; }
}
internal class SuggesterElementConverter : JsonConverter
{
public override bool CanConvert(Type t)
{
return t == typeof(MySuggesterElement) || t == typeof(MySuggesterElement?);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.String:
case JsonToken.Date:
var stringValue = serializer.Deserialize<string>(reader);
return new MySuggesterElement { JsonString = stringValue };
case JsonToken.StartObject:
var objectValue = serializer.Deserialize<MySuggester>(reader);
return new MySuggesterElement { MySuggester = objectValue };
}
throw new Exception("Cannot unmarshal type MySuggesterElement");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (MySuggesterElement)untypedValue;
if (value.JsonString != null)
{
serializer.Serialize(writer, value.JsonString);
return;
}
if (value.MySuggester != null)
{
serializer.Serialize(writer, value.MySuggester);
return;
}
throw new Exception("Cannot marshal type CollationElements");
}
public static readonly SuggesterElementConverter Singleton = new SuggesterElementConverter();
}
public class AutocompleteConverter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
SuggesterElementConverter.Singleton
}
};
}
var results = JsonConvert.DeserializeObject<AutocompleteResultsInfo>(resultJson, AutocompleteConverter.Settings);
Many thanks for your help.
Kind Regerds,
Wojciech

You could decode the "mySuggester" as a dictionary:
public class Suggest
public class Suggest
{
[JsonProperty("mySuggester")]
public Dictionary<string, MySuggester> MySuggester { get; set; }
}
Then you'll be able to access the suggestions with the query string parameter:
var variablePropertyName = "Ext";
var result = JsonConvert.DeserializeObject<AutocompleteResultsInfo>(_json);
var suggestions = result.Suggest.MySuggester[variablePropertyName].Suggestions;
if you don't know the property name you could also look it up in the dictionary:
var variablePropertyName = result.Suggest.MySuggester.Keys.First();
Working example:
https://dotnetfiddle.net/GIKwLs

If you don't need to deserialize the whole json string you can use a JsonTextReader. Example:
private static IEnumerable<string> GetTerms(string json)
{
using (JsonTextReader reader = new JsonTextReader(new StringReader(json)))
{
while (reader.Read())
{
if (reader.TokenType == JsonToken.PropertyName && reader.Value.Equals("term"))
{
string term = reader.ReadAsString();
yield return term;
}
}
}
}
Using the code:
string json = #"{
""responseHeader"": {
""zkConnected"": true,
""status"": 0,
""QTime"": 2
},
""suggest"": {
""mySuggester"": {
""Ext"": {
""numFound"": 10,
""suggestions"": [
{
""term"": ""Extra Community"",
""weight"": 127,
""payload"": """"
},
{
""term"": ""External Video block"",
""weight"": 40,
""payload"": """"
},
{
""term"": ""Migrate Extra"",
""weight"": 9,
""payload"": """"
}
]
}
}
}
}";
IEnumerable<string> terms = GetTerms(json);
foreach (string term in terms)
{
Console.WriteLine(term);
}

If you only need the object containing the term, and nothing else,
you could work with the JSON values directly by using the JObject interface in JSON.Net.
var parsed = JObject.Parse(jsonString);
var usingLinq = (parsed["suggest"]["mySuggester"] as JObject)
.Descendants()
.OfType<JObject>()
.Where(x => x.ContainsKey("term"));
var usingJsonPath = parsed.SelectTokens("$.suggest.mySuggester.*.*[?(#.term)]")
.Cast<JObject>();

Related

How to skip property name in json serialization?

I am trying to convert List to json. Structure is as follow:
public class ResourceCollection
{
public string Name { get; set; }
public Resources Resources { get; set;}
}
public class Resources
{
public string en { get; set; }
}
List<ResourceCollection> liResourceName = new List<ResourceCollection>();
//section to add the objects in list
string json = JsonConvert.SerializeObject(liResourceName, Newtonsoft.Json.Formatting.Indented);
This is producing the result as expected:
[
{
"Name": "Hello",
"Resources":
{
"en": "Hello"
}
},
{
"Name": "World",
"Resources":
{
"en": "World"
}
}
]
How can I get the results like:-
{
"Hello": {
"en": "Hello"
},
"World": {
"en": "World"
}
}
You will need to create a custom JsonConverter that knows how to handle serializing ResourceCollection
public class ResourceCollectionConverter : JsonConverter<List<ResourceCollection>> {
public override bool CanRead {
get {
return false; //because ReadJson is not implemented
}
}
public override List<ResourceCollection> ReadJson(JsonReader reader, Type objectType, List<ResourceCollection> existingValue, bool hasExistingValue, JsonSerializer serializer) {
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, List<ResourceCollection> value, JsonSerializer serializer) {
var obj = new JObject(); // { }
foreach (var item in value) {
//{ "Hello" : { "en": "Hello" } }
obj[item.Name] = JObject.FromObject(item.Resources);
}
obj.WriteTo(writer);
}
}
Use the converter so that JsonConvert knows how to handle the serialization.
For example
List<ResourceCollection> liResourceName = new List<ResourceCollection>();
liResourceName.Add(new ResourceCollection { Name = "Hello", Resources = new Resources { en = "Hello" } });
liResourceName.Add(new ResourceCollection { Name = "World", Resources = new Resources { en = "World" } });
var formating = Newtonsoft.Json.Formatting.Indented;
var converter = new ResourceCollectionConverter();
string json = JsonConvert.SerializeObject(liResourceName, formating , converter);

Proper way for child-classes to have common properties with different types

I have a JSON object that will be formatted as such:
{
"myNodes": [
{
"param1": 1,
"param2": "myValue2a",
"param3": {
"myParam3param": 0
}
},
{
"param1": 1,
"param2": "myValue2b",
"param3": [
{
"myItemA": "abc",
"myItemB": "def",
"myItemC": "0"
}]
},
{
"param1": 1,
"param2": "myValue2c",
"param3": [
{
"myItemA": "ghi",
"myItemB": "jkl",
"myItemC": "0"
}]
}]
}
In C#, I'm wondering how to structure response objects to handle this. I'm guessing I'll have some sort of parent class or interface that contains param1, param2, and param3. However, param3 will need to be declared as type "object", and sometimes it will be an object w/ myParam3param, and other times it will be a list of things. I'm also guessing that I should use child classes that determine what type param3 is.
Is this possible? How should this be accomplished... an abstract class or interface?
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class ChildClasses
{
[JsonProperty("myNodes")]
public List<MyNode> MyNodes { get; set; }
}
public partial class MyNode
{
[JsonProperty("param1")]
public long Param1 { get; set; }
[JsonProperty("param2")]
public string Param2 { get; set; }
[JsonProperty("param3")]
public Param3Union Param3 { get; set; }
}
public partial class Param3Element
{
[JsonProperty("myItemA")]
public string MyItemA { get; set; }
[JsonProperty("myItemB")]
public string MyItemB { get; set; }
[JsonProperty("myItemC")]
public string MyItemC { get; set; }
}
public partial class PurpleParam3
{
[JsonProperty("myParam3param")]
public long MyParam3Param { get; set; }
}
public partial struct Param3Union
{
public List<Param3Element> Param3ElementArray;
public PurpleParam3 PurpleParam3;
public bool IsNull => Param3ElementArray == null && PurpleParam3 == null;
}
public partial class ChildClasses
{
public static ChildClasses FromJson(string json) => JsonConvert.DeserializeObject<ChildClasses>(json, QuickType.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this ChildClasses self) => JsonConvert.SerializeObject(self, QuickType.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters = {
new Param3UnionConverter(),
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class Param3UnionConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Param3Union) || t == typeof(Param3Union?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
switch (reader.TokenType)
{
case JsonToken.StartObject:
var objectValue = serializer.Deserialize<PurpleParam3>(reader);
return new Param3Union { PurpleParam3 = objectValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<List<Param3Element>>(reader);
return new Param3Union { Param3ElementArray = arrayValue };
}
throw new Exception("Cannot unmarshal type Param3Union");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (Param3Union)untypedValue;
if (value.Param3ElementArray != null)
{
serializer.Serialize(writer, value.Param3ElementArray); return;
}
if (value.PurpleParam3 != null)
{
serializer.Serialize(writer, value.PurpleParam3); return;
}
throw new Exception("Cannot marshal type Param3Union");
}
}`

C# parsing JSON array overwrites values on the list

Im having problem with deserializing json file into my class.
Json file looks like this:
{
"DataFile": {
"header":{
"version": "123",
"date": "01.01.01",
},
"customer": {
"fID": "12-35-58",
"nameCust": "CompanyName",
"adressCust":{
"zip": "0000",
"city": "Foovile",
"streetNr": "1",
"post": "FoovilePost",
"street": "DunnoStr",
},
},
"content": [
{
"invoice":{
"DFID":"538",
},
"invoice":{
"DFID":"500",
},
"invoice":{
"DFID":"550",
},
"receipt":{
"DFID":"758",
},
"receipt":{
"DFID":"75",
},
}
],
}
}
Everything before content array deserializes fine,so to keep things clear I'll skip parts of my class. Relevant bit looks like this:
class DFFile
{
public DF Df { get; set; }
}
class DF
{
public Header header { get; set; }
public Customer customer { get; set; }
public List<DFContent> content { get; set; }
}
class DFContent
{
public Invoice invoice { get; set; }
public Receipt receipt { get; set; }
}
class Invoice
{
public int DFID { get; set; }
}
class Receipt
{
public int DFID { get; set; }
}
And i deserialize into DFFile instance like this:
DFFile sample = JsonConvert.DeserializeObject<DFFile>(json);
My problem is that it deserializes without errors but sample.DF.content have only one element that have invoice and receipt with last id of each type. And result I'm looking for is list where there is new element for each item of json content array.
I can change my class but way this json is build is set in stone, can't do anything about it and have to deal with it.
Is there any way to stop it changing last element of content and add new one instead?
Your content is set up to hold an array but you only have one item in it which has three invoice and two receipt values. Assuming you want your DFContent to hold either a receipt or invoice, change it to look like this:
"content": [
{"invoice":{
"DFID":"538",
}},
{"invoice":{
"DFID":"500",
}},
{"invoice":{
"DFID":"550",
}},
{"receipt":{
"DFID":"758",
}},
{"receipt":{
"DFID":"75",
}}
],
Since you cannot change the JSON I assumed that you can change your class structure.
Please consider this classes:
class DF
{
// Other properties...
[JsonConverter(typeof(FunnyListConverter))]
public List<Content> content { get; set; }
}
class Content
{
public int DFID { get; set; }
}
class Invoice : Content
{
}
class Receipt : Content
{
}
And here is the FunnyListConverter:
public class FunnyListConverter : 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)
{
var content = new List<Content>();
var currentType = "";
Invoice currentInvoice = null;
Receipt currentReceipt = null;
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndArray)
{
break;
}
if (reader.Value?.ToString() == "invoice")
{
currentInvoice = new Invoice();
currentType = "invoice";
}
if (reader.Value?.ToString() == "receipt")
{
currentReceipt = new Receipt();
currentType = "receipt";
}
if (reader.Path.Contains("DFID") && reader.Value?.ToString() != "DFID")
{
switch (currentType)
{
case "invoice":
currentInvoice.DFID = int.Parse(reader.Value.ToString());
content.Add(currentInvoice);
currentInvoice = null;
break;
case "receipt":
currentReceipt.DFID = int.Parse(reader.Value.ToString());
content.Add(currentReceipt);
currentReceipt = null;
break;
}
}
}
return content;
}
public override bool CanConvert(Type objectType)
{
return true;
}
if (reader.Value?.ToString() == "receipt")
{
currentReceipt = new Receipt();
currentType = "receipt";
}
if (reader.Path.Contains("DFID") && reader.Value?.ToString() != "DFID")
{
switch (currentType)
{
case "invoice":
currentInvoice.DFID = int.Parse(reader.Value.ToString());
content.Add(currentInvoice);
currentInvoice = null;
break;
case "receipt":
currentReceipt.DFID = int.Parse(reader.Value.ToString());
content.Add(currentReceipt);
currentReceipt = null;
break;
}
}
}
return content;
}
public override bool CanConvert(Type objectType)
{
return true;
}
}

JsonConvert DeserializeObject case sensitive [duplicate]

This question already has an answer here:
Json.NET case-sensitive deserialization
(1 answer)
Closed 6 years ago.
I am trying to deserialize a string content into an object, but I want the content to be case sensitive. The code should only succeed if the string has lower case properties and fail if it has upper case properties. Following is the class:
internal class ResponseList
{
[DataMember]
[JsonProperty]
internal List<Response> Value { get; set; }
}
internal class Response
{
[DataMember]
[JsonProperty]
internal string Id { get; set; }
[DataMember]
[JsonProperty]
internal string Location { get; set; }
[DataMember]
[JsonProperty]
internal PlanClass Plan { get; set; }
}
internal class PlanClass
{
[DataMember]
[JsonProperty]
internal string Name { get; set; }
[DataMember]
[JsonProperty]
internal string Product { get; set; }
[DataMember]
[JsonProperty]
internal string Publisher { get; set; }
}
Following is the code I have. But this is not case-sensitive. It is succeeding for both upper and lowercase:
string content = File.ReadAllText(contentFilePath);
JsonSerializerSettings jsonSerializerSettings1 = new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
ResponseList response = (ResponseList)JsonConvert.DeserializeObject(contentResourceOutput, typeof(ResponseList), Constants.JsonSerializerSettings);
The code should only succeed if the content is:
{
"value": [
{
"id": "id1",
"location": "location1",
"plan": {
"name": "free",
"product": "product1",
"publisher": "publisher1"
}
}
]
}
and fail if even if one of the keys is uppercase. E.g.
{
"value": [
{
"Id": "id1",
"Location": "location1",
"plan": {
"Name": "free",
"product": "product1",
"publisher": "publisher1"
}
}
]
}
Notice that only the Keys/Property names should be lower case. The values can be upper case.
Is there a way to make JsonConvert.Deserializeobject case sensitive?
You can write a custom converter to handle this use case. In regards to your need for a recursive inspection of all key names, I used the fantastic WalkNode answer given by Thymine here.
var json = #"{""id"": ""id1"",""name"": ""name1"",""type"": ""type1""}";
var json2 = #"{""id"": ""id1"",""Name"": ""name1"",""type"": ""type1""}";
JsonSerializerSettings settings = new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Converters = new List<JsonConverter> { new CamelCaseOnlyConverter() }
};
var response = JsonConvert.DeserializeObject<Response>(json, settings);
var response2 = JsonConvert.DeserializeObject<Response>(json2, settings);
public class CamelCaseOnlyConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var token = (JObject)JToken.Load(reader);
var isCamelCased = true;
WalkNode(token, null,
t =>
{
var nameFirstChar = t.Name[0].ToString();
if (!nameFirstChar.Equals(nameFirstChar.ToLower(),
StringComparison.CurrentCulture))
{
isCamelCased = false;
return;
}
});
if (!isCamelCased) return null;
return token.ToObject(objectType);
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
JObject o = (JObject)JToken.FromObject(value);
o.WriteTo(writer);
}
private static void WalkNode(JToken node,
Action<JObject> objectAction = null,
Action<JProperty> propertyAction = null)
{
if (node.Type == JTokenType.Object)
{
if (objectAction != null) objectAction((JObject)node);
foreach (JProperty child in node.Children<JProperty>())
{
if (propertyAction != null) propertyAction(child);
WalkNode(child.Value, objectAction, propertyAction);
}
}
else if (node.Type == JTokenType.Array)
foreach (JToken child in node.Children())
WalkNode(child, objectAction, propertyAction);
}
}
The first string will return a hydrated object. The second string will terminate early, returning null.

JSON serialization using newtonsoft in C#

I have the following model structure.
public class ReferenceData
{
public string Version { get; set; }
public List<DataItem> Data { get; set; }
}
public class DataItem
{
public Dictionary<string, string> Item { get; set; }
}
In the dictionary i'm adding the key value pair and serializing with KeyValuePairConverter setting.
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
Converters = new List<JsonConverter>() { new KeyValuePairConverter() }
};
var object = Newtonsoft.Json.JsonConvert.SerializeObject(
referenceData,
Formatting.None,
settings
);
And the output is,
{
"data":[
{
"item":{
"ShortDescription":"Lorem ipssumm",
"Title":"some text",
"PlanType":"ZEROP",
}
},
{
"item":{
"ShortDescription":"Lorem ipssumm",
"Title":"some text",
"PlanType":"ZEROP",
}
},
{
"item":{
"ShortDescription":"Lorem ipssumm",
"Title":"some text",
"PlanType":"ZEROP",
}
}
]
}
If we don't want item to be showed in the serialized string, what setting needs to be done in JsonSerializerSettings or is there any other way to do that.
Please note that i can not change the model structure as it is required.
output should be :
{
"data":[
{
"ShortDescription":"Lorem ipssumm",
"Title":"some text",
"PlanType":"ZEROP"
},
{
"ShortDescription":"Lorem ipssumm",
"Title":"some text",
"PlanType":"ZEROP"
},
{
"ShortDescription":"Lorem ipssumm",
"Title":"some text",
"PlanType":"ZEROP"
}
]
}
You do not need nested generic collections if you use Json.NET 5.0 release 5 or later version.
You can use JsonExtensionDataAttribute so that Item dictionary's keys and values will be serialized as a part of parent object.
public class ReferenceData
{
public string version { get; set; }
public List<DataItem> data { get; set; }
}
public class DataItem
{
[JsonExtensionData]
public IDictionary<string, object> item { get; set; }
}
// ...
var referenceData = new ReferenceData {
version = "1.0",
data = new List<DataItem> {
new DataItem {
item = new Dictionary<string, object> {
{"1", "2"},
{"3", "4"}
}
},
new DataItem {
item = new Dictionary<string, object> {
{"5", "8"},
{"6", "7"}
}
}
}
};
Console.WriteLine(JsonConvert.SerializeObject(referenceData));
Pay attention that you need Dictionary<string, object> instead of Dictionary<string, string>.
Here is the result I get:
{
"version": "1.0",
"data": [
{
"1": "2",
"3": "4"
},
{
"5": "8",
"6": "7"
}
]
}
Obviously, you can remove Version property to get the expected result.
Read more here:
http://james.newtonking.com/archive/2013/05/08/json-net-5-0-release-5-defaultsettings-and-extension-data
If you change like this result will be what you expected;
public class ReferenceData
{
public string Version { get; set; }
public List<Dictionary<string, string>> Data { get; set; }
}
possible other solution is;
ReferenceData r = new ReferenceData();
r.Data = new List<DataItem>();
r.Data.Add(new DataItem { Item = new Dictionary<string, string>() { { "1", "2" }, { "3", "4" } } });
var anon = new
{
data = r.Data.ToList().Select(x =>
{
dynamic data = new ExpandoObject();
IDictionary<string, object> dictionary = (IDictionary<string, object>)data;
foreach (var key in x.Item.Keys)
dictionary.Add(key, x.Item[key]);
return dictionary;
}
)
};
var result = JsonConvert.SerializeObject(anon);
result :
{
"data": [
{
"1": "2",
"3": "4"
}
]
}
If you can't change the C# can use you a View model and use an appropriate structure. It's probably simpler than changing JSON settings, easier to return to and more explicit:
public class ReferenceData
{
public string Version { get; set; }
public List<Dictionary<string, string>> Data { get; set; }
}
Should serialise as you require.
You could implement a custom behaviour as follows:
class Program {
static void Main(string[] args) {
var referenceData = new ReferenceData() {
Data = new List<DataItem>() {
new DataItem(){
Item = new Dictionary<string,string>() {
{"ShortDescription", "Lorem ipssumm"},
{"Title", "some text"},
{"PlanType", "ZEROP"},
}
},
new DataItem(){
Item = new Dictionary<string,string>() {
{"ShortDescription", "Lorem ipssumm"},
{"Title", "some text"},
{"PlanType", "ZEROP"},
}
},
new DataItem(){
Item = new Dictionary<string,string>() {
{"ShortDescription", "Lorem ipssumm"},
{"Title", "some text"},
{"PlanType", "ZEROP"},
}
}
}
};
var settings = new JsonSerializerSettings {
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
Converters = new List<JsonConverter>() { new KeyValuePairConverter(), new CustomJsonSerializableConverter() }
};
File.WriteAllText("hello.json", Newtonsoft.Json.JsonConvert.SerializeObject(
referenceData,
Formatting.Indented,
settings
));
}
}
public class ReferenceData {
public string Version { get; set; }
public List<DataItem> Data { get; set; }
}
[CustomJsonSerializable]
public class DataItem {
public Dictionary<string, string> Item { get; set; }
public static void WriteJson(JsonWriter writer, DataItem value, JsonSerializer serializer) {
serializer.Serialize(writer, value.Item);
}
public static DataItem ReadJson(JsonReader reader, DataItem existingValue, JsonSerializer serializer) {
DataItem result = new DataItem();
result.Item = serializer.Deserialize<Dictionary<string, string>>(reader);
return result;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class CustomJsonSerializableAttribute : Attribute {
public readonly string Read;
public readonly string Write;
public CustomJsonSerializableAttribute()
: this(null, null) {
}
public CustomJsonSerializableAttribute(string read, string write) {
this.Read = read;
this.Write = write;
}
}
public class CustomJsonSerializableConverter : JsonConverter {
public override bool CanConvert(Type objectType) {
return objectType.GetCustomAttribute(typeof(CustomJsonSerializableAttribute), false) != null;
}
public override bool CanWrite {
get {
return true;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
if(value != null) {
var t = value.GetType();
var attr = (CustomJsonSerializableAttribute)t.GetCustomAttribute(typeof(CustomJsonSerializableAttribute), false);
var #delegate = t.GetMethod(attr.Write ?? "WriteJson", new Type[] { typeof(JsonWriter), t, typeof(JsonSerializer) });
#delegate.Invoke(null, new object[] { writer, value, serializer });
} else {
serializer.Serialize(writer, null);
}
}
public override bool CanRead {
get {
return true;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
var t = existingValue.GetType();
var attr = (CustomJsonSerializableAttribute)t.GetCustomAttribute(typeof(CustomJsonSerializableAttribute), false);
var #delegate = t.GetMethod(attr.Read ?? "ReadJson", new Type[] { typeof(JsonReader), t, typeof(JsonSerializer) });
return #delegate.Invoke(null, new object[] { reader, existingValue, serializer });
}
}

Categories

Resources