Hope that someone can help me with this seemingly simple problem. In short, I want to use camel case for all property names, except when the property is a DataTable. In that case, I want the column names to be unchanged when serialized.
I have a rather deep object structure that I serialize to JSON, using JSON.NET. I want the property names to be camel case, and I dont want to put attributes on every class, so I use the following code:
public static class JsonHelper
{
private static DefaultContractResolver _contractResolver = new DefaultContractResolver()
{
NamingStrategy = new CamelCaseNamingStrategy()
};
public static string SerializeObjectToJson(object obj)
{
string json = JsonConvert.SerializeObject(obj, new JsonSerializerSettings
{
ContractResolver = _contractResolver
});
return json;
}
}
...
string json = JsonHelper.SerializeObjectToJson(myDataDTO);
That works fine. However, I also have ADO.NET DataTable properties, and in that case I want to keep the case of the column names unchanged.
Simplified example here:
public class MySampleDTO
{
public Object Data { get; init; }
public int SomeInteger { get; init; }
}
...
// Get table with columns "Period", "Sales" and "Expenses"
DataTable myDataTable = ...
MySampleDTO dto = new MySampleDTO() {
Data = myDataTable,
SomeInteger = 3
}
string json = JsonHelper.SerializeObjectToJson(dto);
The resulting JSON becomes:
{
"data": [
{
"period": "2004",
"sales": 400,
"expenses": 700
},
{
"period": "2005",
"sales": 500,
"expenses": 900
},
{
"period": "2006",
"sales": 720,
"expenses": 420
}
],
"someInteger": 3
}
Obviously, the column names of the data table are serialized as camelcase which is not what I want (but I want "SomeInteger" to be camel case)
Any ideas how I can solve this?
Json.NET
uses a DataTableConverter
to convert a DataTable
to and from JSON
.
This one shares the same serializer with all other converters that are being used for the (de)serialization of an object, in this case one configured with a camel case naming strategy.
In order to apply a different naming strategy for a DataTable
, you can implement a custom DataTableConverter
in which you configure the serializer
to use a different IContractResolver
/DefaultContractResolver
without the camel case naming strategy while (de)serializing the DataTable
.
Alternativelly you might set up a whole different serializer that you pass to base.WriteJson
and base.ReadJson
.
public class CustomDataTableConverter : DataTableConverter
{
private static readonly IContractResolver _contractResolver = new DefaultContractResolver();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contractResolver = serializer.ContractResolver;
try
{
serializer.ContractResolver = _contractResolver;
base.WriteJson(writer, value, serializer);
}
finally
{
serializer.ContractResolver = contractResolver;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var contractResolver = serializer.ContractResolver;
object value = null;
try
{
serializer.ContractResolver = _contractResolver;
value = base.ReadJson(reader, objectType, existingValue, serializer);
}
finally
{
serializer.ContractResolver = contractResolver;
}
return value;
}
}
You add this CustomDataTableConverter
to the converters of your JsonSerializerSettings
.
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver()
{
NamingStrategy = new CamelCaseNamingStrategy()
},
Formatting = Newtonsoft.Json.Formatting.Indented
};
settings.Converters.Add(new CustomDataTableConverter());
var datatable = new DataTable();
datatable.Columns.Add("Period", typeof(string));
datatable.Columns.Add("Sales", typeof(int));
datatable.Columns.Add("Expenses", typeof(int));
datatable.Rows.Add("2004",400, 700);
datatable.Rows.Add("2005", 500, 900);
datatable.Rows.Add("2006", 720, 420);
var dto = new MySampleDto
{
Data = datatable,
SomeInteger = 3
};
var json = JsonConvert.SerializeObject(dto, settings);
The columns of the DataTable
in the JSON
result will now be Period
, Sales
and Expenses
as expected.
{
"data": [
{
"Period": "2004",
"Sales": 400,
"Expenses": 700
},
{
"Period": "2005",
"Sales": 500,
"Expenses": 900
},
{
"Period": "2006",
"Sales": 720,
"Expenses": 420
}
],
"someInteger": 3
}