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);
Related
This question already has answers here:
How to handle both a single item and an array for the same property using JSON.net
(9 answers)
Closed 3 years ago.
I am trying to process an object that can be either an array of object or just the object. Using the code below only works when the naics is an object and not an array. What am I doing wrong?
Here is the shortest example I can come up with:
[{
"section": "52.219-1.b",
"naics": [{
"naicsName": "Engineering Services",
"isPrimary": true,
"ExcpCounter": 1,
"isSmallBusiness": "Y",
"naicsCode": 541330
},
{
"naicsName": "Military and Aerospace Equipment and Military Weapons",
"isPrimary": true,
"ExcpCounter": 2,
"isSmallBusiness": "Y",
"naicsCode": 541330
}
]
},
{
"section": "52.219-1.b",
"naics": {
"naicsName": "Janitorial Services",
"isPrimary": true,
"isSmallBusiness": "Y",
"naicsCode": 561720
}
}
]
I will only have one of the types but I forced two in an array to force it into Quick Type.
My classes are:
[JsonProperty("naics", NullValueHandling = NullValueHandling.Ignore)]
public AnswerNaics Naics { get; set; }
public partial struct AnswerNaics
{
public AnswerNaic Naic;
public AnswerNaic[] NaicArray;
public static implicit operator AnswerNaics(AnswerNaic Naic) => new AnswerNaics { Naic = Naic };
public static implicit operator AnswerNaics(AnswerNaic[] NaicArray) => new AnswerNaics { NaicArray = NaicArray };
}
public partial class AnswerNaic
{
[JsonProperty("naicsName")]
public string NaicsName { get; set; }
[JsonProperty("hasSizeChanged")]
public string HasSizeChanged { get; set; }
[JsonProperty("isPrimary")]
public bool IsPrimary { get; set; }
[JsonProperty("ExcpCounter", NullValueHandling = NullValueHandling.Ignore)]
public long? ExcpCounter { get; set; }
[JsonProperty("isSmallBusiness")]
public string IsSmallBusiness { get; set; }
[JsonProperty("naicsCode")]
public string NaicsCode { get; set; }
}
internal class NaicsConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(AnswerNaics) || t == typeof(AnswerNaics?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.StartObject:
var objectValue = serializer.Deserialize<AnswerNaic>(reader);
return new AnswerNaics { Naic = objectValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<AnswerNaic[]>(reader);
return new AnswerNaics { NaicArray = arrayValue };
}
throw new Exception("Cannot unmarshal type AnswerNaics");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (AnswerNaics)untypedValue;
if (value.NaicArray != null)
{
serializer.Serialize(writer, value.NaicArray);
return;
}
if (value.Naic != null)
{
serializer.Serialize(writer, value.Naic);
return;
}
throw new Exception("Cannot marshal type Naics");
}
public static readonly NaicsConverter Singleton = new NaicsConverter();
}
I have more object or array nodes, but I am just trying to figure out one to be able to apply to all of them.
Since you cannot change the incoming JSON, you're going to need a custom converter instead. Something like this for example:
public class NaicsConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Naics);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
var naics = new Naics();
switch (reader.TokenType)
{
case JsonToken.StartObject:
// We know this is an object, so serialise a single Naics
naics.Add(serializer.Deserialize<Naic>(reader));
break;
case JsonToken.StartArray:
// We know this is an object, so serialise multiple Naics
foreach(var naic in serializer.Deserialize<List<Naic>>(reader))
{
naics.Add(naic);
}
break;
}
return naics;
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And the supporting classes:
public class Root
{
public string Section { get; set; }
[JsonConverter(typeof(NaicsConverter))]
public Naics Naics { get; set; }
}
// This isn't ideal, but it's quick and dirty and should get you started
public class Naics : List<Naic>
{
}
public class Naic
{
public string NaicsName { get; set; }
public bool IsPrimary { get; set; }
public string IsSmallBusiness { get; set; }
public long NaicsCode { get; set; }
}
And finally, to deserialise:
var settings = new JsonSerializerSettings {Converters = {new NaicsConverter()}};
var root = JsonConvert.DeserializeObject<Root[]>(Json, settings);
Now your object will get serialised into the list, but as a single item.
You can solve this using a dynamic field in your class.
Consider this JSON:
[
{
"field1": "val1",
"nested": [
{
"nestedField": "val2"
},
{
"nestedField": "val3"
}
]
},
{
"field1": "val4",
"nested":
{
"nestedField": "val5"
}
}
]
nested field is first an array with 2 objects and in the second appearance a single object. (similar to the JSON you posted)
So the class representation would look like:
public class RootObject
{
public string field1 { get; set; }
public dynamic nested { get; set; }
public List<NestedObject> NestedObjects
{
get
{
if(nested is JArray)
{
return JsonConvert.DeserializeObject<List<NestedObject>>(nested.ToString());
}
var obj = JsonConvert.DeserializeObject<NestedObject>(nested.ToString());
return new List<NestedObject> { obj };
}
}
}
public class NestedObject
{
public string nestedField { get; set; }
}
The Deserialization code is trivial using Newtonsoft JSON:
var objectList = JsonConvert.DeserializeObject<List<RootObject>>("some_json");
foreach(var v in objectList)
{
foreach(var n in v.NestedObjects)
{
Console.WriteLine(n.nestedField);
}
}
The only change is an implementation of NestedObjects ready only property. It check if the dynamic object is JArray or object. In any case, it returns a List of nested objects.
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>();
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;
}
}
I am trying to deserialize an Object like this.
My issue is that it is blowing up trying to deserialize the inner items.
{
"outeritems": [{
"inneritems": [
[{
"itemid": "1"
}, {
"itemid": "2"
}]
]
}]
}
I have already tried
public List<List<inneritems>> inneritems{ get; set; }
also
public List<inneritems> inneritems{ get; set; }
I'm thinking this might have to have a custom JSON converter
Actually vikas is close in anwering your question.
public class Outeritem
{
public List<List<object>> inneritems { get; set; }
}
public class RootValue
{
public List<Outeritem> outeritems { get; set; }
}
[TestMethod]
public void SerializeAndDeserializeTest()
{
var expected = "{\"outeritems\":[{\"inneritems\":[[{\"itemid\":\"1\"},{\"itemid\":\"2\"}]]}]}";
var rootValue = new RootValue
{
outeritems = new List<Outeritem>
{
new Outeritem
{
inneritems = new List<List<object>> {
new List<object> { new {itemid = "1"},new {itemid = "2"} }
}
}
}
};
var actual = JsonConvert.SerializeObject(rootValue);
Assert.AreEqual(expected, actual);
}
Try this
public class Inneritem
{
public String itemid { get; set; }
}
public class Outeritem
{
public List<Inneritem> inneritems { get; set; }
}
public class RootValue
{
public List<Outeritem> outeritems { get; set; }
}
I had to create a custom Newtonsoft JSON Converter
This is what I did.
Created A JsonCreationConverter Base Class
public abstract class JsonCreationConverter<T> : JsonConverter
{
protected abstract T Create(Type objectType, JObject jObject);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanWrite is false. The type will skip the converter.");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
JObject jObject = JObject.Load(reader);
T target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override bool CanWrite
{
get { return false; }
}
}
Created A Costume Converter that inherited my base class
public class OuterConverter :
JsonCreationConverter<OuterItems>
{
protected override OuterItems Create(Type objectType, JObject jObject)
{
OuterItems outeritems =
new OuterItems();
var properties = jObject.Properties().ToList();
outeritems.InnerItems = GetInnerItems((object)properties[0].Value);
return outeritems;
}
// Need to iterate through list so creating a custom object
private List<List<InnerItems>> GetInnerItems(object propertyValue)
{
string sinneritems = "";
object inneritems = propertyValue;
sinneritems = String.Format("{0}", inneritems);
sinneritems = sinneritems.Insert(1, "{ \"Items\": [");
sinneritems = sinneritems.Substring(1, sinneritems.Length - 1);
sinneritems = sinneritems.Remove(sinneritems.Length - 1);
sinneritems += "]}";
dynamic results = JObject.Parse(sinneritems);
List<List<InnerItems>> innerItemsList = new List<List<InnerItems>>();
List<InnerItems> linnerItems = new List<InnerItems>();
foreach (var items in results.Items)
{
foreach (var item in items)
{
string sItem = String.Format("{0}", item);
InnerItems ninneritems = Newtonsoft.Json.JsonConvert.DeserializeObject<InnerItems>(sItem);
linnerItems.Add(ninneritems);
}
innerItemsList.Add(linnerItems);
}
return innerItemsList;
}
}
Append the New Converter on the Newtonsoft Deserializer
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result.ToString(),
new OuterConverter());
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 });
}
}