I have a collection of name / value pairs where they are defined with the words name and value just like a Key/Value object, i.e.
[{"Name":"ActivityId","DataType":1,"Value":"a7868f8c-07ac-488d-a414-714527c2e76f"},
{"Name":"Address1","DataType":2,"Value":"123 Main St"}]
If I had an object like:
class Request
{
public Guid ActivityId { get; set; }
public string Address1 {get; set; }
}
How can I deserialize this to the class above?
Should I consider a custom converter? Does Json.NET have something built-in? Is there a way to decorate the properties with an attribute that I'm missing? Would it be easier to customize the serialization?
I'm trying to avoid pulling the data for each property from a Dictionary, which would be the easy route, but would require me to do this with each custom implementation. I would prefer to do this in a base class in a single method using Json.NET (or something in the .NET framework).
I've searched quite a bit, and most examples are real name/value pairs, not prefixed with name and value, i.e.
[{"ActivityId":"a7868f8c-07ac-488d-a414-714527c2e76f"}]
Any ideas?
This can be done in a straightforward manner with a custom JsonConverter like the one below. The converter works by first transforming the array of name-value pairs into a JObject with properties mirroring the pairs, then populating the target object from the JObject using the serializer's built-in Populate method.
class NameValueConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load the array of name-value pairs and transform into a JObject.
// We are assuming all the names will be distinct.
JObject obj = new JObject(
JArray.Load(reader)
.Children<JObject>()
.Select(jo => new JProperty((string)jo["Name"], jo["Value"]))
);
// Instantiate the target object and populate it from the JObject.
object result = Activator.CreateInstance(objectType);
serializer.Populate(obj.CreateReader(), result);
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// WriteJson is not called when CanWrite returns false
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
// We only want this converter to handle classes that are expressly
// marked with a [JsonConverter] attribute, so return false here.
// (CanConvert is not called when [JsonConverter] attribute is used.)
return false;
}
}
To use the converter, just add a [JsonConverter] attribute to the target class:
[JsonConverter(typeof(NameValueConverter))]
class Request
{
public Guid ActivityId { get; set; }
public string Address1 {get; set; }
}
Then, you can deserialize as you normally would:
Request req = JsonConvert.DeserializeObject<Request>(json);
Fiddle: https://dotnetfiddle.net/tAp1Py
Related
Say an application has serializer settings with CamelCasePropertyNamesContractResolver and I have a model:
public class Model
{
public int Id { get; set; }
[JsonProperty(Name = "content_text")]
public string Text { get; set; }
}
I'd like to find a way to get the resulting name a serializer would generate on serialize for each property, so that following would be true:
string JsonName<T>(string cSharpPropertyName)
{
//
}
JsonName<Model>(nameof(Model.Id)) == "id";
JsonName<Model>(nameof(Model.Text)) == "content_text";
I'm writing a custom deserializer (JSON converter) for a custom type and I'd like to match my Model properties with JSON input without being case insensitive but with the current serializer strategy.
I had idea to just deserialize each incoming JToken wrapped in a new JSON object to a c# object and check resulting property name but it doesn't seem to be very efficient. Is there a better way to do this?
You can use the contract resolver itself to do this job.
Make your JsonName method accept an IContractResolver as a parameter along with the property name. The implementation might look something like this:
public static string JsonName<T>(string cSharpPropertyName, IContractResolver resolver) where T : class
{
var contract = resolver.ResolveContract(typeof(T)) as JsonObjectContract;
var property = contract?.Properties.FirstOrDefault(p => p.UnderlyingName == cSharpPropertyName);
return property?.PropertyName;
}
Inside your JsonConverter, you can get the contract resolver from the serializer, which is provided as a parameter to ReadJson:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var resolver = serializer.ContractResolver;
...
}
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'm using Newtownsoft JSON.Net and want to add custom attributes, then have a JSONConverter that deals with these custom attributes. Currently the way to do this is [JsonConverter(typeof(CustomJsonConverter))]. But I don't want this converter to always be called, only when I pass it into JsonConvert.SerializeObject(...). This is because this class is being used by two different paths, one of which needs to modify the JSON based on the properties and the other doesn't.
In other words, I only want these attributes to be considered when I explicitly tell my code to consider them. They should be ignored when Newtonsoft is doing its default serialization. How can I achieve this?
Example:
class Foo {
[CustomAttributeToAddMyExtraProperty]
public int Bar;
[JsonProperty('default')]
public int Baz;
}
If I just use the default Newtonsoft JSON.Net serialize, I should get
{
"Bar":value1,
"default":value2
}
But if I pass in my custom converter explicitly, I should get this:
{
"Bar":value1,
"default":value2,
"MyExtraProperty":value3
}
Notice that the JSON.Net attributes are always used.
UPDATE
I have tried this:
namespace Project1
{
class Class1
{
static void Main()
{
Class2 foo = new Class2();
Console.WriteLine(JsonConvert.SerializeObject(foo, new JsonSerializerSettings() { ContractResolver = new BlahResolver() }));
Console.Read();
}
}
class Class2
{
[Blah]
public int one = 1;
[JsonProperty]
public int two = 2;
[Blah]
public string three = "3";
}
internal class BlahAttribute : Attribute
{
}
class BlahResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
var attr = property.AttributeProvider.GetAttributes(typeof(BlahAttribute), true);
if (attr.Count == 1)
{
property.Converter = new BlahConverter();
}
return property;
}
}
class BlahConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsValueType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return existingValue;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken token = JToken.Parse("{ \"foo\":\"a\", \"bar\":34 }");
token.WriteTo(writer);
}
}
}
Output:
{"one":{"foo":"a","bar":34},"two":2,"three":{"foo":"a","bar":34}}
I'm able to successfully identify properties with my custom attribute and redirect them to my custom converter. The issue now is the converter is adding properties as if one is a complex type. I want to add the properties in the top level JSON, not as properties of one.
Output I want:
{"one":1, "foo":"a","bar":34,"two":2,"three":3,"foo":"a","bar":34}
I realize that this has multiple properties with the same name. I don't know if that's valid or not, but eventually the object value will be included in the name (ex. one_foo, one_bar), so you can disregard that.
With your update, you're very close to getting what you need to work for this scenario. At this point, your only issue is in the WriteJson method.
All you need to do at this point is simply:
writer.WritePropertyName("propertyName");
writer.WriteValue("propertyValue");
If you need to use a different serializer depending on the current execution path, you are correct that the attribute route will not work
Instead pass in the currently desired converter during the serialization process:
string json = JsonConvert.SerializeObject(employee, Formatting.Indented, new MyCustomConverter(typeof(MyType)));
Source
Temporary note: This is NOT a duplicate of the above mentioned post
Let's say I have a server-side class structure like this.
public class Test
{
// this can be any kind of "Tag"
public object Data { get; set; }
}
public class Other
{
public string Test { get; set; }
}
Now a string like this is coming from let's say the client.
{"Data": [{$type: "MyProject.Other, MyProject", "Test": "Test"}] }
When I try to deserialize this into a Test instance, I get a result where the Tag property is a JToken instead of some kind of collection, for example ArrayList or List<object>.
I understand that Json.NET cannot deserialize into a strongly typed list, but I'd expect that it respects that it's at least a list.
Here is my current deserialization code.
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto,
};
var str = "{\"Data\": [{\"$type\": \"MyProject.Other, MyProject\", \"Test\": \"Test\"}] }";
var test = JsonConvert.Deserialize<Test>(str, settings);
// this first assertion fails
(test.Data is IList).ShouldBeTrue();
(((IList)test.Data)[0] is Other).ShouldBeTrue();
I'm aware of the fact that if I serialize such a structure, then by default I'll get a { $type: ..., $values: [...]} structure in the JSON string instead of a pure array literal, and that will indeed properly deserialize. However, the client is sending a pure array literal, so I should be able to handle that in some way.
I managed to put together a JsonConverter to handle these kind of untyped lists. The converter applies when the target type is object. Then if the current token type is array start ([) it will force a deserialization into List<object>. In any other case it will fall back to normal deserialization.
This is a first version which passes my most important unit tests, however as I'm not a Json.NET expert, it might break some things unexpectedly. Please if anyone sees anything what I didn't, leave a comment.
public class UntypedListJsonConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartArray)
{
return serializer.Deserialize(reader);
}
return serializer.Deserialize<List<object>>(reader);
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(object);
}
}
Usage example:
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto,
Converters = new[] { new UntypedListJsonConverter() }
};
var str = "{\"Data\": [{\"$type\": \"MyProject.Other, MyProject\", \"Test\": \"Test\"}] }";
var test = JsonConvert.Deserialize<Test>(str, settings);
// now these assertions pass
(test.Data is IList).ShouldBeTrue();
(((IList)test.Data)[0] is Other).ShouldBeTrue();
Try this:
public class Test
{
public Dictionary<string, List<Other>> Data { get; } = new Dictionary<string, List<Other>>();
}
You need to set up the class you are trying to fill from json data to match as closely to the json structure. From the looks of it, the json looks a dictionary where the keys are strings and the values are arrays of Other objects.
I have created several classes that map to Schema.org objects;
public class Thing {
public virtual string FullSchemaType { get { return "[schema.org]Thing"; } }
}
public class CreativeWork : Thing {
public override string FullSchemaType {get {return string.Join(":", base.FullSchemaType, "CreativeWork"); } }
[JsonProperty("author")]
public string Author { get;set; }
// etc
}
public class MediaObject : CreativeWork {
public override string FullSchemaType {get {return string.Join(":", base.FullSchemaType, "MediaObject"); } }
[JsonProperty("duration")]
public float? Duration { get;set; }
}
I have a factory class that creates e.g. a MediaObject, sets its properties. The FullSchemaType property is a Schema.org compliant way of noting its type. I am putting these objects into a database, serialising them using Json.NET 6.0.3.
I want to deserialize them into the correct C# objects. The standard Json.Net way to do this is to use TypeNameHandling - but that inserts a $type property into the serialised Json, which isn't ideal as we have several different applications interfacing with this database.
Is there a way to tell Json.NET to look at my FullSchemaType property for type binding information?
You can use JsonSerializerSettings to customise parameters of serialization: setting TypeNameHandling = TypeNameHandling.None allows you not to include $type information in serialized Json, and if you have any further problems with, say, customised converters you can use JsonSerializerSettings.IList<JsonConverter> settings to specify ones.
More detailed info is here: JsonSerializerSettings
and here: Newtonsoft.Json.TypeNameHandling
And here is a good example of JsonCreationConverter, you can overrride method ReadJson in your own converter, based on JsonConverter like this:
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);
if (jObject[FullSchemaType] == {your_media_object_type_name})
MediaObject target = Create(objectType, jObject);
else if (jObject[FullSchemaType] == {your_creative_work_type_name})
CreativeWork target = Create(objectType, jObject);
else
Thing target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}