I am working on a C# application which has a browser integrated with it.
The browser will send some data to C# in json format.
Some of the fields from json can be dserialized using javascript deserializer, but I have some data for which a custom deserializer is required, I need to register a deserializer for that but the thing is the custom deserializer must be called only for those special data and the default javascript deserializer must be called for other data, the special data can be identified from there target field's data type / name in C#. How can I achieve this.
something like this.
public class example
{
public string abc;
public someOtherDataType xyz;
public void example()
{
serializer = new JavaScriptSerializer();
// receive json string
serializer.RegisterConverters(new JavaScriptConverter[]
{
new System.Web.Script.Serialization.CS.CustomConverter()
});
//call deserializer
}
}
The json string will be something like
{
"abc" : "valueabc"
"xyz" : "valueXYZ"
}
Now the custom deserializer must be called only during deserializing xyz and default must be called for abc.
Thank you.
The difficulty here is that a JavaScriptConverter allows you to map a JSON object from and to a c# class -- but in your JSON, "xyz" is just a string, not an object. Thus you can't specify a converter for someOtherDataType and instead must specify converters for every class that contains an instance of someOtherDataType.
(Note that the custom converter functionality in Json.NET does not have this restriction. If you were willing to switch to that library you could write a JsonConverter converting all uses of someOtherDataType from and to a JSON string.)
To write such a JavaScriptConverter:
Override JavaScriptConverter.Deserialize
Create a second Dictionary<string, Object> filtering out the fields requiring custom conversion.
Call new JavaScriptSerializer.ConvertToType<T> to deserialize the standard fields from the filtered dictionary.
Manually convert the remaining fields.
Override SupportedTypes to return the container type.
Thus, in your example, you could do:
public class example
{
public string abc;
public someOtherDataType xyz;
}
// Example implementation only.
public class someOtherDataType
{
public string SomeProperty { get; set; }
public static someOtherDataType CreateFromJsonObject(object xyzValue)
{
if (xyzValue is string)
{
return new someOtherDataType { SomeProperty = (string)xyzValue };
}
return null;
}
}
class exampleConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get { return new[] { typeof(example) }; }
}
// Custom conversion code below
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var defaultDict = dictionary.Where(pair => pair.Key != "xyz").ToDictionary(pair => pair.Key, pair => pair.Value);
var overrideDict = dictionary.Where(pair => !(pair.Key != "xyz")).ToDictionary(pair => pair.Key, pair => pair.Value);
// Use a "fresh" JavaScriptSerializer here to avoid infinite recursion.
var value = (example)new JavaScriptSerializer().ConvertToType<example>(defaultDict);
object xyzValue;
if (overrideDict.TryGetValue("xyz", out xyzValue))
{
value.xyz = someOtherDataType.CreateFromJsonObject(xyzValue);
}
return value;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
}
And then, to test:
public class TestClass
{
public static void Test()
{
// receive json string
string json = #"{
""abc"" : ""valueabc"",
""xyz"" : ""valueXYZ""
}";
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[]
{
new exampleConverter()
});
var example = serializer.Deserialize<example>(json);
Debug.Assert(example.abc == "valueabc" && example.xyz.SomeProperty == "valueXYZ"); // No assert
}
}
Related
I have a DTO class like this:
public class MyDto
{
public KeyValuePair<string, string> Identifier { get; set; }
public double Value1 { get; set; }
public string Value2 { get; set; }
}
And two instances could look like this:
var results = new List<MyDto>
{
new MyDto
{
Identifier = new KeyValuePair<string, string>("Car", "Ford"),
Value1 = 13,
Value2 = "A"
},
new MyDto
{
Identifier = new KeyValuePair<string, string>("Train", "Bombardier"),
Value1 = 14,
Value2 = "B"
},
};
When serializing the results within ASP.NET Web API (which uses Json.NET), the output looks like this:
[
{
"Identifier": {
"Key": "Car",
"Value": "Ford"
},
"Value1": 13,
"Value2": "A"
},
{
"Identifier": {
"Key": "Train",
"Value": "Bombardier"
},
"Value1": 14,
"Value2": "B"
}
]
But I'd like to have it that way:
[
{
"Car": "Ford",
"Value1": 13,
"Value2": "A"
},
{
"Train": "Bombardier",
"Value1": 14,
"Value2": "B"
}
]
How can I achieve this? Do I have to write a custom JsonConverter and do everything by hand? Since the DTO class is located in a separate assembly not having access to Json.NET, the usage of specific Json.NET attributes is not possible.
I'm not bound to using KeyValuePair<string, string>. I only need a data structure that allows me to have a flexible attribute name.
Instead of a KeyValuePair<>, you can easily serialize a dictionary as part of a parent object by applying [JsonExtensionData] like so:
public class MyDto
{
[JsonExtensionData]
public Dictionary<string, object> Identifier { get; set; }
However, you have stated that the usage of specific Json.NET attributes is not possible. But since you can generally modify your DTO you could mark it with a custom extension data attribute and then handle that attribute in either a custom generic JsonConverter or a custom contract resolver.
Firstly, for an example of using a custom extension data attribute with a custom JsonConverter, see JsonTypedExtensionData from this answer to How to deserialize a child object with dynamic (numeric) key names?
Secondly, if you prefer not to use a converter, to handle a custom extension data attribute with a custom contract resolver, first define the following contract resolver:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class MyJsonExtensionDataAttribute : Attribute
{
}
public class MyContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
if (contract.ExtensionDataGetter == null && contract.ExtensionDataSetter == null)
{
var dictionaryProperty = contract.Properties
.Where(p => typeof(IDictionary<string, object>).IsAssignableFrom(p.PropertyType) && p.Readable && p.Writable)
.Where(p => p.AttributeProvider.GetAttributes(typeof(MyJsonExtensionDataAttribute), false).Any())
.SingleOrDefault();
if (dictionaryProperty != null)
{
dictionaryProperty.Ignored = true;
contract.ExtensionDataGetter = o =>
((IDictionary<string, object>)dictionaryProperty.ValueProvider.GetValue(o)).Select(p => new KeyValuePair<object, object>(p.Key, p.Value));
contract.ExtensionDataSetter = (o, key, value) =>
{
var dictionary = (IDictionary<string, object>)dictionaryProperty.ValueProvider.GetValue(o);
if (dictionary == null)
{
dictionary = (IDictionary<string, object>)this.ResolveContract(dictionaryProperty.PropertyType).DefaultCreator();
dictionaryProperty.ValueProvider.SetValue(o, dictionary);
}
dictionary.Add(key, value);
};
}
contract.ExtensionDataValueType = typeof(object);
// TODO set contract.ExtensionDataNameResolver
}
return contract;
}
}
Then modify your DTO as follows:
public class MyDto
{
[MyJsonExtensionData]
public Dictionary<string, object> Identifier { get; set; }
public double Value1 { get; set; }
public string Value2 { get; set; }
}
And serialize as follows, caching a static instance of your resolver for performance:
static IContractResolver resolver = new MyContractResolver();
// And later
var settings = new JsonSerializerSettings
{
ContractResolver = resolver,
};
var json = JsonConvert.SerializeObject(results, Formatting.Indented, settings);
Notes:
The MyJsonExtensionData property's type must be assignable to type IDictionary<string, object> and have a public, parameterless constructor.
Naming strategies for extension data property names are not implemented.
Json.NET serializes extension data attributes at the end of each object whereas your question shows the custom attributes at the beginning. Since a JSON object is defined to be an unordered set of name/value pairs by the standard I think this should not matter. But if you require the custom properties at the beginning of your object, you may need to use a custom converter rather than a custom contract resolver.
Demo fiddle here.
Edit: this answer applies to the question as originally asked; it has subsequently been edited, and may now be less useful
You may need to use a dictionary that has a single element, i.e. Dictionary<string,string>, which does serialize in the way you want; for example:
var obj = new Dictionary<string, string> { { "Car", "Ford" } };
var json = JsonConvert.SerializeObject(obj);
System.Console.WriteLine(json);
Since your MyDto class is in a separate assembly for which you have limitations in the kinds of changes you can make, then yes, I think your best bet is to create a custom converter for the class. Something like this should work:
public class MyDtoConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(MyDto);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
MyDto dto = (MyDto)value;
writer.WriteStartObject();
writer.WritePropertyName(dto.Identifier.Key);
writer.WriteValue(dto.Identifier.Value);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(typeof(MyDto));
foreach (JsonProperty prop in contract.Properties.Where(p => p.PropertyName != nameof(MyDto.Identifier)))
{
writer.WritePropertyName(prop.PropertyName);
writer.WriteValue(prop.ValueProvider.GetValue(dto));
}
writer.WriteEndObject();
}
public override bool CanRead => false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use it you will need to add the converter to your Web API configuration:
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new MyDtoConverter());
Here is a working demo of the converter in a console app: https://dotnetfiddle.net/DksgMZ
A server is returning a JSON string value which is a URL query string:
{
"parameters": "key1=value1&key2=value2"
}
I have a property set up to receive this, and convert it into a Dictionary as part of the deserialisation process:
Property with JsonConverter attribute:
[JsonConverter(typeof(QueryStringToDictionaryJsonConverter))]
public Dictionary<string, string> Parameters { get; set; }
Converter:
public class QueryStringToDictionaryJsonConverter : JsonConverter<Dictionary<string, string>> {
public override Dictionary<string, string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
var queryString = reader.GetString();
if (string.IsNullOrEmpty(queryString)) return null;
return QueryHelpers.ParseQuery(queryString).ToDictionary(e => e.Key, e => string.Join(",", e.Value.ToArray()));
}
...
}
This should work.
But it's not even getting to my converter.
From what I can tell, JsonSerializer.DeserializeAsync<T>(myJson) is seeing that the type of property is a Dictionary, and so it tries to parse the value as such on its own, and fails (the resulting exception is an 'invalid cast' as it tries to GetEnumerable() etc). A breakpoint in my converter never even gets hit.
I can get it to work by making the property an object and then casting to a Dictionary later where it's used, but that's an ugly solution.
Is there a way to force JsonSerializer.DeserializeAsync<T>(myJson) to just use my converter, without it trying to be smart on its own?
(I'm using Microsoft's System.Text.Json in .NET Core 3)
OK, so this could be a bug in System.Text.Json.
Here's the workaround I'm currently using for anyone else needing a solution.
First, I set up two properties for deserialisation, using [JsonPropertyName] and [JsonIgnore]:
[JsonPropertyName("parameters"), JsonConverter(typeof(QueryStringToDictionaryJsonConverter))]
public object ParametersObject { get; set; }
[JsonIgnore]
public Dictionary<string, string> Parameters => ParametersObject as Dictionary<string, string>;
And then in the JsonConverter, I allow object as the type:
public override bool CanConvert(Type typeToConvert) {
if (typeToConvert == typeof(object)) return true;
return base.CanConvert(typeToConvert);
}
Consumers of my deserialised class just use the Parameters property, which will continue to work just fine if and when this bug is fixed and I change the class back to how I'd like it.
I would create a wrapper and create a converter for the wrapper.
[JsonConverter( typeof( QueryStringDictionaryConverter ) )]
class QueryStringDictionary : Dictionary<string,string> { }
class QueryStringDictionaryConverter : JsonConverter<QueryStringDictionary>
{
...
}
class MyClass
{
public QueryStringDictionary Parameters { get; set; }
}
Alternatively you could use JsonSerializerOptions
class MyOtherClass
{
public Dictionary<string,string> Parameters { get; set; }
}
MyOtherClass Deserialize( string json )
{
var options = new JsonSerializerOptions
{
Converters = { new QueryStringToDictionaryJsonConverter() }
};
return JsonSerializer.Deserialize<MyOtherClass>( json, options );
}
A potential problem with this approach is that the converter would be used on all Dictionary<string,string> properties, which may not be intended. It would work fine for the simple example in the original question.
I am currently working on a Custom JSON converter to be used in a WebAPI project. The requirement is - I have a DTO object having some properties. The APIs can be consumed by multiple clients. Depending upon a client few of my DTO Entities might have some additional data apart from the properties already present in the DTO Model. I need to create a custom JSON converter to Serialize and Deserialize this data.
//DTO
class AbcDTO
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public List<AdditionalProperty> AdditionalData { get; set; }
}
//AdditionalProperty class
class AdditionalProperty
{
public string Name { get; set; }
public object Value { get; set; }
}
//Request JSON Body
{
"Prop1": "Val1",
"Prop2": "Val2",
"AdditionalProp3": "Val3",
"AdditionalProp4": "Val4"
}
//After Deserialization the object should be as below
AbcDTO dto = {
Prop1 = "Val1",
Prop2 = "Val2",
AdditionalData = [
{ Name = "AdditionalProp3", Value = "Val3" },
{ Name = "AdditionalProp4", Value = "Val4" }]
}
//After Serialization of the above dto object the JSON should convert back to the Request JSON Body format
We don't want to use the JsonExtensionData attribute provided by Newtonsoft.JSON as we would need to keep the property as Dictionary<string, JToken> -- but we don't want to pass JToken to below layers.
Created a custom JSON converter -
class CustomJsonConverter : JsonConverter
{
bool _canWrite = true;
bool _canRead = true;
public override bool CanConvert(Type objectType)
{
return typeof(IEntity).IsAssignableFrom(objectType);
}
public override bool CanWrite
{
get
{
return _canWrite;
}
}
public override bool CanRead
{
get
{
return _canRead;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
PropertyInfo[] availablePropertyNames = objectType.GetProperties();
List<AdditionalProperties> additionalData = new List<AdditionalProperties>();
IEntity obj;
_canRead = false;
obj = (IEntity)jObject.ToObject(objectType);
_canRead = true;
IEnumerable<JProperty> properties = jObject.Properties();
foreach (JProperty prop in properties)
{
if (availablePropertyNames.Count(x => x.Name.Equals(prop.Name)) == 0)
{
AdditionalProperties addProp = new AdditionalProperties
{
Name = prop.Name,
Value = prop.Value.ToObject<object>(),
};
additionalData.Add(addProp);
}
}
obj.AdditionalData = additionalData;
return obj;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
IEntity obj = (IEntity)value;
List<AdditionalProperties> additionalData = obj.AdditionalData;
JObject jObj;
_canWrite = false;
jObj = (JObject)JToken.FromObject(obj);
_canWrite = true;
jObj.Remove("AdditionalData");
foreach (AdditionalProperties data in additionalData)
{
jObj.Add(data.Name, JToken.FromObject(data.Value));
}
jObj.WriteTo(writer);
}
}
WebAPI ContractResolver creates 1 JSON converter per Entity. Now the issue is _canRead and _canWrite are not thread-safe. Need to use them to use the base implementation provided by Newtonsoft. If we don't use them, the ToObject and FromObject method again calls the custom converter methods internally resulting in infinite recursion. Using them with logs, reduces performance. Is there any way we can create a custom converter using the base implementation of Newtonsoft.JSON serialization/deserialization without using canRead and canWrite flags?
I can also have reference type child properties - say Person contains Address. I want to capture additional data for both Parent and Child entities. The additional data will not contain data of reference type.
It's possible to disable the converter using a thread static variable or ThreadLocal<T> member, as shown in JSON.Net throws StackOverflowException when using JsonConvert or Generic method of modifying JSON before being returned to client. However, I'd like to suggest a simpler way of solving your problem.
You wrote, We dont want to use the JsonExtensionData attribute provided by Newtonsoft.JSON as we need to keep the property as Dictionary and we dont want to pass JToken to below layers. It is not necessary for the extension data dictionary to have values of type JToken. Values of type object are supported for extension data dictionaries, e.g.:
class AbcDTO
{
public AbcDTO() { this.AdditionalData = new Dictionary<string, object>(); }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
[JsonExtensionData]
public Dictionary<string, object> AdditionalData { get; private set; }
}
When the extension data dictionary is of type Dictionary<string, object>, Json.NET will deserialize JSON primitive values to their equivalent .Net primitives -- string, bool, long and so on -- rather than to JValue objects. Only when encountering an additional property whose value is a JSON object or array will a JToken be added to the dictionary, in which case you can use the answers from How do I use JSON.NET to deserialize into nested/recursive Dictionary and List? to convert the JToken to a conventional .Net type. (However, your question states that The additional data will not contain data of reference type, so this should not be necessary.)
Using [JsonExtensionData] in this manner completely avoids the need for a converter while also deserializing primitives as per your requirements, and thus seems much simpler than the original design shown in the question.
Sample .Net fiddle demonstrating that extension properties can be deserialized into AbcDTO and asserting that none of them are of type JToken.
I have a JSON object that I get from my REST API server that looks like this:
"settings": {
"assets.last_updated_at": "2016-08-24T23:40:26.442Z",
"data.version": 34
}
Normally I'd deserialize the object to a Dictionary of string to object, but I'd like to provide helper methods to get strongly typed versions of the values. I'm using JSON.NET to deserialize the JSON object. Here's what I've got so far:
[JsonConverter(typeof(SettingsJsonConverter))]
public class Settings
{
[JsonIgnore]
public Dictionary<string, string> Entries { get; private set; }
public Settings()
{
this.Entries = new Dictionary<string, string>();
}
}
In order to wrap the dictionary within a class, it seems like I needed to create a custom JsonConverter, which currently looks like this:
public class SettingsJsonConverter : JsonConverter
{
#region implemented abstract members of 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)
{
JObject settingsObj = JObject.Load(reader);
Settings settings = new Settings();
foreach(KeyValuePair<string, JToken> entry in settingsObj)
{
settings.Entries.Add(entry.Key, entry.Value.ToString());
}
return settings;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Settings);
}
#endregion
}
Now obviously I could create methods in my Settings class like, GetDate that takes the value for a given key and creates a new DateTime object with the ISO string value. However, JSON.NET already provides facilities to do what I want to do, so I'd rather use its built-in deserialization for any values I iterate over, instead of doing it myself.
It seems as though the place to do this would be inside the foreach loop in SettingsJsonConverter#ReadJson, but I'm not quite sure how to get the Type (not a JTokenType, a standard C# Type) of a given JToken so I can pass it into JToken#ToObject(Type). Or, how can I iterate over a JsonReader to populate my internal Entries dictionary with already deserialized values, such that I just need to do simple casts in my helper methods in Settings? Here's how I'd like my Settings class to look:
[JsonConverter(typeof(SettingsJsonConverter))]
public class Settings
{
[JsonIgnore]
public Dictionary<string, object> Entries { get; private set; }
public Settings()
{
this.Entries = new Dictionary<string, object>();
}
public DateTime GetDate(string key)
{
return (DateTime)this.Entries[key];
}
}
Am I even doing this correctly? It almost feels like there's some simple way I'm overlooking.
You can directly deserialize into a Dictionary<string, JToken> and use type casts as you need them so you don't need a Settings class or a custom converter. It doesn't look as nice as your solution, but the code is simpler:
var settings = JsonConvert.DeserializeObject<Dictionary<string, JToken>>(json);
var date = (DateTime) settings["key"];
If you want to keep your Settings class, I'd suggest that you use the above line to deserialize the dictionary and create a new constructor in Settings with a dictionary parameter, so you end up with two constructors, one that creates a new dictionary, and one that uses a given dictionary to create the new one. (Don't re-use the parameter dictionary itself, you never know what the caller will do to the dictionary afterwards.)
public Settings(Dictionary<string, JToken> dict)
{
Entries = new Dictionary<string, JToken>(dict);
}
By the way, you can remove the JsonIgnore attribute because you have a custom converter class.
First of all we have to make a model for your corresponding json.
{
"settings": {
"assets.last_updated_at": "2016-08-24T23:40:26.442Z",
"data.version": 34
}
}
CSharp Model:
public class Settings
{
[JsonProperty("assets.last_updated_at")]
public string assets_last_updated_at { get; set; }
[JsonProperty("data.version")]
public int data_version { get; set; }
// Add other property here...
}
public class RootObject
{
public Settings settings { get; set; }
}
Deserialize Json string to Model :
RootObject rootObject = JsonConvert.DeserializeObject<RootObject>("Put your Json String");
Hope this help.
I want to send parameter-settings from a backendapplication to the frontend. I also need to be able to have different type of parameters (Portnumbers, folders, static strings and such).
So I've designed a baseclass, Parameter as such:
public abstract class Parameter
{
public abstract bool isValid();
}
Let's say that we have two types of folder parameters:
public abstract class Folder : Parameter
{
public string folderName { get; set; }
protected Folder(string folderName)
{
this.folderName = folderName;
}
}
public class ReadWriteFolder : Folder
{
public ReadWriteFolder(string folderName) : base(folderName)
{
}
public override bool isValid()
{
return isReadable() && isWritable();
}
}
public class ReadFolder : Folder
{
public ReadFolder(string folderName) : base(folderName)
{
}
public override bool isValid()
{
return isReadable();
}
}
This is used from a WebAPI, so this is my controller:
public Dictionary<String, Parameter> Get()
{
Dictionary<String, Parameter> dictionary = new Dictionary<String, Parameter>();
dictionary.Add("TemporaryFiles", new ReadWriteFolder("C:\\temp\\"));
dictionary.Add("AnotherTemporaryFiles", new ReadWriteFolder("D:\\temp\\"));
return dictionary;
}
This yields the following JSON-serialisation:
{"TemporaryFiles":{"folderName":"C:\\temp\\"},"AnotherTemporaryFiles":{"folderName":"D:\\temp\\"}} which seems reasonable.
My question is this: How can I deserialize this back into the original types? Or change the serialization into something that is more easy to deserialize?
What are you using for serialization? If it's JSON.Net (which many here would suggest!), there's a number of realted questions:
how to deserialize JSON into IEnumerable with Newtonsoft JSON.NET
But the crux is the type name handling, which will decorate the elements with the type information to be able to deserialize them:
JsonSerializerSettings settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
};
string strJson = JsonConvert.SerializeObject(dictionary, settings);
And then you should be able to deserialize directly.
var returnDictionary = JsonConvert.DeserializeObject<Dictionary<String, Parameter>>(strJson, settings)