Serialize IEnumerable<IDictionary<string, object>> doesn't work with DefaultNamingStrategy - c#

By default I use CamelCasePropertyNamesContractResolver in my project for serialize to json. But I want to change this strategy for one property.
public class ViewTable
{
public int Id { get; set; }
[JsonProperty(NamingStrategyType = typeof(DefaultNamingStrategy), ItemTypeNameHandling = TypeNameHandling.None, TypeNameHandling = TypeNameHandling.None)]
public IEnumerable<IDictionary<string, object>> Rows { get; set; }
}
So when I serialize this object I expect to get such json:
"result": {
"id": 15,
"rows": [
{
"SessionData_Department": "",
"SystemData_SerialNumber": "1"
}
]
}
But I got:
"result": {
"id": 15,
"Rows": [ //There must be a lowercase!!!
{
"sessionData_Department": "", //There must be a uppercase!!!
"systemData_SerialNumber": "1"
}
]
}
I have such json settings in my project:
var settings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
settings.Formatting = Formatting.Indented;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
settings.TypeNameHandling = TypeNameHandling.Auto;
How can I tell json serializer use DefaultNamingStrategy for IDictionary?

I'm not sure that there is a setting from out-of-the box. But you can do it by extending JsonConverter and using DefaultContractResolver:
public class RowsConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
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 settings = new JsonSerializerSettings()
{
ContractResolver = new DefaultContractResolver()
};
writer.WriteRawValue(JsonConvert.SerializeObject(value, settings));
}
}
Then change Rows property to:
[JsonProperty(ItemConverterType = typeof(RowsConverter))]
public IEnumerable<IDictionary<string, object>> Rows { get; set; }

You will have to extend DefaultNamingStrategy:
public class CamelCaseDictionaryKeyNamingStrategy : DefaultNamingStrategy
{
public CamelCaseDictionaryKeyNamingStrategy() : base() { this.ProcessDictionaryKeys = true; }
public override string GetDictionaryKey(string key)
{
if (ProcessDictionaryKeys && !string.IsNullOrEmpty(key))
{
if (char.ToUpperInvariant(key[0]) != key[0])
{
var builder = new StringBuilder(key) { [0] = char.ToUpperInvariant(key[0]) };
return builder.ToString();
}
}
return key;
}
}
Then use it like:
IDictionary<string, object> idict = new Dictionary<string, object>();
idict.Add("sessionData_Department", "1");
idict.Add("systemData_SerialNumber", "1");
IEnumerable<IDictionary<string, object>> row = new List<IDictionary<string, object>> { idict };
var val = new ViewTable
{
Id = 15,
Rows = row
};
var cc = new CamelCasePropertyNamesContractResolver
{
NamingStrategy = new CamelCaseDictionaryKeyNamingStrategy()
};
JsonSerializerSettings config = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = cc,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
TypeNameHandling = TypeNameHandling.Auto
};
string js = JsonConvert.SerializeObject(val, config);
Output:
I have updated Rows to:
public class ViewTable
{
public int Id { get; set; }
[JsonProperty(PropertyName = "rows", NamingStrategyType = typeof(DefaultNamingStrategy), ItemTypeNameHandling = TypeNameHandling.None, TypeNameHandling = TypeNameHandling.None)]
public IEnumerable<IDictionary<string, object>> Rows { get; set; }
}

Related

JsonConvert List to Json Properties

I would want to convert an object with a list property into a json object. The list property should not be parsed directly, instead the items in the list should be added to the json object as properties. The property names should be custom and have the index as suffix. Only the serialzation is needed.
Example:
class Foo{
public bool Boolean { get; set; }
public List<Bar> List { get; set; }
// 50+ additional properties to be serialized.
}
class Bar{
public string Value{ get; set; }
}
Code:
var bar = new Foo();
bar.Boolean = true;
bar.List = new List<Bar>() {
new Bar(){ Value = "Foo1" },
new Bar(){ Value = "Bar2" },
new Bar(){ Value = "Foo3" }
};
JsonConvert.Serialize(bar)
Desired output:
{
"Boolean": true,
"property1" : "Foo1",
"property2" : "Bar2",
"property3" : "Foo3"
}
Note that, in my real classes, there are over 50 other properties for Foo so practically I cannot write a JsonConverter<Foo> that serializes each one manually.
Here is one other approach to produce expected JSON without JSON converter
Redefine the POCO object as below
public class Foo
{
public bool Boolean;
[JsonIgnore]
public List<Bar> List;
[JsonExtensionData]
public Dictionary<string, JToken> Values
{
get
{
return List.Select((v, i) => new KeyValuePair<string, JToken>($"Property{i + 1}", JValue.CreateString(v.Value))).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
}
}
public class Bar{
public string Value{ get; set; }
}
Then use JSON library to serialize as below
var bar = new Foo();
bar.Boolean = true;
bar.List = new List<Bar>() {
new Bar(){ Value = "Foo1" },
new Bar(){ Value = "Bar2" },
new Bar(){ Value = "Foo3" }
};
var json = JsonConvert.SerializeObject(bar, Formatting.Indented);
Console.WriteLine(json);
Sample fiddle: https://dotnetfiddle.net/jgWlLB
You can generate the JSON you require with a custom JsonConverter. Since your "real" data model has 50+ properties, you can use Json.NET's own contract metadata to loop through the properties of Foo and serialize them appropriately. The following does the trick:
public class ItemListContainerConverter<TContainer, TItem> : JsonConverter<TContainer>
{
protected virtual string PropertyName => "property";
public override void WriteJson(JsonWriter writer, TContainer value, JsonSerializer serializer)
{
var contract = serializer.ContractResolver.ResolveContract(value.GetType()) as JsonObjectContract;
if (contract == null)
throw new JsonSerializationException("value is not a JSON object");
JsonProperty itemListProperty = null;
writer.WriteStartObject();
foreach (var property in contract.Properties.Where(p => ShouldSerialize(p, value)))
{
var propertyValue = property.ValueProvider.GetValue(value);
if (propertyValue == null && (serializer.NullValueHandling == NullValueHandling.Ignore || property.NullValueHandling == NullValueHandling.Ignore))
continue;
if (typeof(IEnumerable<TItem>).IsAssignableFrom(property.PropertyType))
{
itemListProperty = (itemListProperty == null ? property : throw new JsonSerializationException("Too many IEnumerable<Bar> properties"));
}
else
{
writer.WritePropertyName(property.PropertyName);
if (propertyValue == null)
writer.WriteNull();
else if (property.Converter != null && property.Converter.CanWrite)
property.Converter.WriteJson(writer, propertyValue, serializer);
else
serializer.Serialize(writer, propertyValue);
}
}
if (itemListProperty != null)
{
var itemList = itemListProperty.ValueProvider.GetValue(value) as IEnumerable<TItem>;
if (itemList != null)
{
var itemContract = serializer.ContractResolver.ResolveContract(typeof(TItem));
var valueMethod = GetItemValueMethod(itemContract);
int i = 1;
foreach (var item in itemList)
{
writer.WritePropertyName(PropertyName + (i++).ToString(CultureInfo.InvariantCulture));
serializer.Serialize(writer, valueMethod(item));
}
}
}
writer.WriteEndObject();
}
protected virtual Func<object, object> GetItemValueMethod(JsonContract itemContract)
{
if (!(itemContract is JsonObjectContract objectContract))
throw new JsonSerializationException("item contract is not a JsonObjectContract");
var property = objectContract.Properties.Single(p => ShouldSerialize(p));
return (o) => property.ValueProvider.GetValue(o);
}
protected virtual bool ShouldSerialize(JsonProperty property) => property.Readable && !property.Ignored;
protected virtual bool ShouldSerialize(JsonProperty property, object value) => ShouldSerialize(property) && (property.ShouldSerialize == null || property.ShouldSerialize(value));
public override TContainer ReadJson(JsonReader reader, Type objectType, TContainer existingValue, bool hasExistingValue, JsonSerializer serializer) =>
// Not requested to be implemnented as per the question.
throw new NotImplementedException();
}
And then you can serialize your model Foo as follows:
var settings = new JsonSerializerSettings
{
Converters = { new ItemListContainerConverter<Foo, Bar>() }
};
var json = JsonConvert.SerializeObject(foo, Formatting.Indented, settings);
Which results in
{
"Boolean": true,
"property1": "Foo1",
"property2": "Bar2",
"property3": "Foo3"
}
Notes:
This solution does not require serialization to an intermediate JToken representation.
The converter ItemListContainerConverter<TContainer, TItem> assumes that the container type TContainer will be serialized as a JSON object that has a single property of type IEnumerable<TItem> and any number of additional properties. The additional properties will be serialized normally while the items of the IEnumerable<TItem> collection will be serialized as "propertyI" properties as required.
It assumes that the item type TItem has a single serializable property. If your TItem has more than one property, you can override GetItemValueMethod() to select the property you wish to serialize as the value of the "propertyI" properties.
Deserialization was not implemented as it was not requested in the question.
Demo fiddle here.
Classes:
public class Foo
{
public bool Boolean;
[JsonIgnore]
public List<Bar> List;
}
class Bar{
string Value{ get; set; }
}
public class FooConverter : JsonConverter<Foo>
{
public override Foo ReadJson(JsonReader reader, Type objectType, Foo existingValue, bool hasExistingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, Foo value, JsonSerializer serializer)
{
var t = JToken.FromObject(value);
JObject o = (JObject)t;
for (int i = 0; i < value.List.Count; i++)
{
o.Add($"prop{i + 1}", new JValue(value.List[i].Value));
}
o.WriteTo(writer);
}
}
Code:
var bar = new Foo();
bar.Boolean = true;
bar.List = new List<Bar>() {
new Bar(){ Value = "Foo1" },
new Bar(){ Value = "Bar2" },
new Bar(){ Value = "Foo3" }
};
JsonConvert.SerializeObject(bar, Formatting.Indented, new FooConverter())
My earlier attempts failed because i tried the simple approach of just passing bar to SerialzeObjects and adding the JsonConverter attribute to the Foo class and so it kept looping on itself.
try this
JObject o = (JObject)JToken.FromObject(bar);
var i = 0;
foreach (var item in (JArray)o["List"])
{
i++;
o.Add("property" + i.ToString(), item["Value"]);
}
o.Remove("List");
o.ToString();
result
{
"Boolean": true,
"property1": "Foo1",
"property2": "Bar2",
"property3": "Foo3"
}

How to deserialize an array of an array of objects using Newtonsoft

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());

How to deserialize a complex separated ("Column"/"Data") json to c# object using json.net

I need to parse the following JSON into a List of PackageEntity Object.
Since this json is divided into Column and Data, I am having trouble to do so in an intelligent way.
The JSON looks like:
{
"COLUMNS": ["NSHIPMENTID", "NSHIPPINGCOMPANYID", "NUSERID", "NWEIGHT", "NHEIGHT"],
"DATA": [
[7474, null, 12363, "16", "2"],
[7593, null, 12363, "64", "7"]
]
}
I would like to deserialize it to a list of the following class:
public class PackageEntity
{
public int NSHIPMENTID { get; set; }
public string NSHIPPINGCOMPANYID { get; set; }
public int NUSERID { get; set; }
public decimal NWEIGHT { get; set; }
public decimal NHEIGHT { get; set; }
}
what i did so far:
JObject JsonDe = JObject.Parse(responseString);
int length = JsonDe.Property("DATA").Value.ToArray().Count();
List<PackageEntity> _list = new List<PackageEntity>();
for (int i = 0; i < length; i++)
{
PackageEntity pD = new PackageEntity();
pD.NSHIPMENTID = JsonDe.Property("DATA").Value.ToArray()[i][0].ToString();
pD.NSHIPPINGCOMPANYID = JsonDe.Property("DATA").Value.ToArray()[i][1].ToString();
pD.NUSERID = JsonDe.Property("DATA").Value.ToArray()[i][2].ToString();
pD.NWEIGHT = JsonDe.Property("DATA").Value.ToArray()[i][3].ToString();
pD.NHEIGHT = JsonDe.Property("DATA").Value.ToArray()[i][4].ToString();
_list.Add(pD);
}
You can use the following generic custom JsonConverter to deserialize your data:
public class ColumnarDataToListConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(List<T>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var list = existingValue as List<T> ?? new List<T>();
var obj = JObject.Load(reader);
var columns = obj["COLUMNS"] as JArray;
var data = obj["DATA"] as JArray;
if (data == null)
return list;
list.AddRange(data
.Select(item => new JObject(columns.Zip(item, (c, v) => new JProperty((string)c, v))))
.Select(o => o.ToObject<T>(serializer)));
return list;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then use it as follows:
var settings = new JsonSerializerSettings { Converters = new[] { new ColumnarDataToListConverter<PackageEntity>() } };
var list = JsonConvert.DeserializeObject<List<PackageEntity>>(responseString, settings);
Note the use of Enumerable.Zip() to pair up the entries in the column array with the entries in each row of the data array into a temporary JObject for deserialization.

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 });
}
}

c# JSON Serialization Use Value Instead of Property Name

I am working on a JSON driven project and I would like to provide the SessionManager object with a dynamic list of permissionst. While I can work with an array of key value pairs for permissions, I was wondering if I could remove the property names so that the key is the Permission value and the value is the IsAllowed value.
public class SessionPermission
{
public string Permission { get; set; }
public bool IsAllowed { get; set; }
}
public class SessionManager
{
public string UserName { get; set; }
public string Password { get; set; }
public List<SessionPermission> Permissions { get; set; }
public void SetPermissions()
{
Permissions = new List<SessionPermission>
{
new SessionPermission {Permission = "CreateUsers", IsAllowed = false},
new SessionPermission {Permission = "EditUsers", IsAllowed = false},
new SessionPermission {Permission = "EditBlog", IsAllowed = true}
};
}
}
When I generate JSON it outputs an array of permissions:
{
"Permission": "CreateUsers",
"IsAllowed": false
},
I would like to know how to override the serialization so that it uses the values instead of the property names.
{
"CreateUsers": false
},
You could use the following custom converter:
public class SessionPermissionConverter : JsonConverter
{
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
var obj = (JObject)JObject.ReadFrom(reader);
JProperty property = obj.Properties().FirstOrDefault();
return new SessionPermission
{
Permission = property.Name,
IsAllowed = property.Value.Value<bool>()
};
}
public override void WriteJson(
JsonWriter writer,
object value,
JsonSerializer serializer)
{
SessionPermission permission = (SessionPermission)value;
JObject obj = new JObject();
obj[permission.Permission] = permission.IsAllowed;
obj.WriteTo(writer);
}
public override bool CanConvert(Type t)
{
return typeof(SessionPermission).IsAssignableFrom(t);
}
public override bool CanRead
{
get { return true; }
}
}
Usage:
var manager = new SessionManager();
manager.SetPermissions();
string json = JsonConvert.SerializeObject(manager, new SessionPermissionConverter());
Sample JSON:
{
"UserName": null,
"Password": null,
"Permissions": [
{
"CreateUsers": false
},
{
"EditUsers": false
},
{
"EditBlog": true
}
]
}
It should work fine going the opposite way as well.
Example: https://dotnetfiddle.net/mfbnuk

Categories

Resources