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
Related
EDIT: I realized it might be imported for context, I'm building this JSON for human readability (I don't see there ever been a business need to parse it back into something meaningful later) which is why I need to keep its formatting as simple as possible
So I have an object I want to serialize to JSON. The serialization is complex, and I've created a helper property to handle that. The helper property successfully returns an object every time that matches that way I want it to be serialized. Unfortunately, the serialization engine still shows the helper functions name, instead of just treating its output as a representation of my objects state.
I'm looking to reach the output below
{
"A" : <The output of the helper property>,
"B" : <The output of the helper property>
}
But I actually get
{
"A" : {
"HelperProperty": <The output of the helper property>
},
"B" : {
"HelperProperty": <The output of the helper property>
}
}
I know in XML formatting there is the [XMLText] attribute that I would apply to the "HelperProperty" property to do this. Is there a similar property in the Newtonsoft landscape? Alternatively, does another Json formatter support this type of operation?
MVCE of what I have now
The object that I am serializing to JSON
[JsonObject(MemberSerialization.OptIn)]
public class SerializationExample
{
[JsonProperty]
public object HelperProperty => (Object)A ?? (Object)B ?? (Object)C ?? "no property specified";
public int? A;
public string B;
public Dictionary<String, int> C;
}
Instantiations of that object and serialization thereof
var a = new SerializationExample() { A = 5 };
var b = new SerializationExample() { B = "five" };
var c = new SerializationExample() { C = new Dictionary<string, int>() { {"number" , 5 } } };
System.IO.File.WriteAllText(#"\users\sidney\desktop\output.json",
JsonConvert.SerializeObject(new Dictionary<string, SerializationExample>() {
{"a",a },{"b",b },{"c",c } }, Formatting.Indented));
How it serializes
{
"a": {
"HelperProperty": 5
},
"b": {
"HelperProperty": "five"
},
"c": {
"HelperProperty": {
"number": 5
}
}
}
How I want it to serialize
{
"a": 5,
"b": "five",
"c": {
"number": 5
}
}
While I couldn't find an exact solution for what I wanted, I was able to find that you were able to control how your json was serialized, courtesy of https://blog.maskalik.com/asp-net/json-net-implement-custom-serialization/.
All I had to do was define a class
public class DirectPropertySerializer : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var name = value as MyClass;
serializer.Serialize(writer, name.HelperProperty);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return true;
}
}
And then add an attribute to MyClass
[JsonConverter(typeof(DirectPropertySerializer))]
(Note, as stated above, I don't see a reason to Read this JSON back and try and parse it, so I'm just leaving the ReadJson function as not implemented.
I extended a dictionary (which is perfect data structure for translations) and added a marker telling what kind of translation will be performed.
internal class Translation : Dictionary<string, string>
{
public string Name { get; set; }
}
However, when I serialize the object, I only get the key-value pairs in my output string. The name doesn't show. I wanted to use the stuff from the goodie bag from uncle Microsoft, i.e. System.Text.Json, so I do the following.
string output = JsonSerializer.Serialize(source);
My suspicion is that I will need to implement a custom serializer but that's way too much hustle for this simple case. My experience tells me there's a neat, smooth approach bundled in the tools (one that I'm simply not aware of).
How to do it? Alternatively, if not possible smoothly, why is it a complex matter (that I'm apparently failing to appreciate)?
I was expecting a JSON on form below.
{
"name": "donkey",
"key1": "value1",
"key2": "value2",
"key3": "value3",
}
I can resolve it by adding an item to my dictionary with key being name and value being donkey, of course. But that pragmatic solution, I prefer to save as my fall-back. At the moment I have some extra time and want to play around with the structure. Also, I can imagine that the name might become an int instead of string or maybe even a more complex structure to describe e.g. timestamp or something. That would totally break the contract of the dictionary (being string-to-string mapping).
This seems to be the design intent -- as with Newtonsoft, JavaScriptSerializer and DataContractJsonSerializer, the dictionary keys and values are serialized, not the regular properties.
As an alternative to extending Dictionary<TKey, TValue>, you can get the JSON you want by encapsulating a dictionary in a container class and marking the dictionary with JsonExtensionDataAttribute:
internal class Translation
{
public string Name { get; set; }
[JsonExtensionData]
public Dictionary<string, object> Data { get; set; } = new Dictionary<string, object>();
}
And then serialize as follows:
var translation = new Translation
{
Name = "donkey",
Data =
{
{"key1", "value1"},
{"key2", "value2"},
{"key3", "value3"},
},
};
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
// Other options as required
WriteIndented = true,
};
var json = JsonSerializer.Serialize(translation, options);
Do note this restriction from the docs
The dictionary's TKey value must be String, and TValue must be JsonElement or Object.
(As an aside, a similar approach would work with Newtonsoft which has its own JsonExtensionDataAttribute. If you are using both libraries, be sure not to get the attributes confused.)
Demo fiddle #1 here.
If this modification to your data model is not convenient, you can introduce a custom JsonConverter<Translation> that (de)serializes a DTO like the model above, then maps the DTO from and to your final model:
internal class Translation : Dictionary<string, string>
{
public string Name { get; set; }
}
internal class TranslationConverter : JsonConverter<Translation>
{
internal class TranslationDTO
{
public string Name { get; set; }
[JsonExtensionData]
public Dictionary<string, object> Data { get; set; }
}
public override Translation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var dto = JsonSerializer.Deserialize<TranslationDTO>(ref reader, options);
if (dto == null)
return null;
var translation = new Translation { Name = dto.Name };
foreach (var p in dto.Data)
translation.Add(p.Key, p.Value?.ToString());
return translation;
}
public override void Write(Utf8JsonWriter writer, Translation value, JsonSerializerOptions options)
{
var dto = new TranslationDTO { Name = value.Name, Data = value.ToDictionary(p => p.Key, p => (object)p.Value) };
JsonSerializer.Serialize(writer, dto, options);
}
}
And then serialize as follows:
var translation = new Translation
{
Name = "donkey",
["key1"] = "value2",
["key2"] = "value2",
["key3"] = "value3",
};
var options = new JsonSerializerOptions
{
Converters = { new TranslationConverter() },
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
// Other options as required
WriteIndented = true,
};
var json = JsonSerializer.Serialize(translation, options);
I find it simpler to (de)serialize to a DTO rather than to work directly with Utf8JsonReader and Utf8JsonWriter as edge cases and naming policies get handled automatically. Only if performance is critical will I work directly with the reader and writer.
With either approach JsonNamingPolicy.CamelCase is required to bind "name" in the JSON to Name in the model.
Demo fiddle #2 here.
I have the following JSON string:
{
"values": {
"details": {
"property1": "94",
"property2": "47",
"property3": "32",
"property4": 1
},
count: 4
}
}
I am going to map this to the following model:
public class Details
{
public string property1 { get; set; }
public string property2 { get; set; }
public string property3 { get; set; }
public int property4 { get; set; }
}
public class Values
{
public Details details { get; set; }
public int count { get; set; }
}
public class RootObject
{
public Values values { get; set; }
}
I want to be able to map the these property names to different names at runtime when deserializing this JSON string like this:
JsonConvert.DeserializeObject<RootObject>(jsonString);
For example, in the deserialization process, I want the deserialize the name of "property1" to "differen_property_name1" or "differen_property_name2" or "differen_property_name3".
Because I am choosing the new name at runtime (the new name to which I will change the "property1" name to), I can't use the solution using JsonPropertyAttribute, as suggested here:
.NET NewtonSoft JSON deserialize map to a different property name
One of the answers of the above question (Jack's answer) uses inheritance of DefaultContractResolver but it doesn't seem to work in that case.
Update
Later on, I needed to serialize the object I got from the deserialization and map the properties to different property names, defined at runtime.
I used the same method as Brian proposed to do the serialization:
I used the dictionary to map my new property names:
var map = new Dictionary<Type, Dictionary<string, string>>
{
{
typeof(Details),
new Dictionary<string, string>
{
{"property1", "myNewPropertyName1"},
{"property2", "myNewPropertyName2"},
{"property3", "myNewPropertyName3"},
{"property4", "myNewPropertyName4"}
}
}
};
and then I used Brian's DynamicMappingResolver to serialize the object like this:
var settings = new JsonSerializerSettings
{
ContractResolver = new DynamicMappingResolver(map)
};
var root = JsonConvert.SerializeObject(myObjectInstance, settings);
You could use a custom ContractResolver to do this. Basically it is the same idea as putting a [JsonProperty] attribute on each class member for which you want to map to a different JSON property name, except you do it programmatically via the resolver. You can pass a dictionary of your desired mappings to the resolver when setting it up just before deserializing.
Here is what the custom resolver code might look like:
class DynamicMappingResolver : DefaultContractResolver
{
private Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap;
public DynamicMappingResolver(Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap)
{
this.memberNameToJsonNameMap = memberNameToJsonNameMap;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
Dictionary<string, string> dict;
string jsonName;
if (memberNameToJsonNameMap.TryGetValue(member.DeclaringType, out dict) &&
dict.TryGetValue(member.Name, out jsonName))
{
prop.PropertyName = jsonName;
}
return prop;
}
}
To use the resolver, first construct a Dictionary<Type, Dictionary<string, string>> containing your mappings. The outer dictionary's key is the the class type(s) whose properties you want to map; the inner dictionary is a mapping of the class property names to JSON property names. You only need to provide a mapping for the properties whose names don't already match the JSON.
So, for example, if your JSON looked like this (notice the changed names of the properties inside the details object)...
{
"values": {
"details": {
"foo": "94",
"bar": "47",
"baz": "32",
"quux": 1
},
count: 4
}
}
...and you wanted to map it to the classes in your question, you would create the dictionary like this:
var map = new Dictionary<Type, Dictionary<string, string>>
{
{
typeof(Details),
new Dictionary<string, string>
{
{"property1", "foo"},
{"property2", "bar"},
{"property3", "baz"},
{"property4", "quux"}
}
}
};
The last step is to set up the serializer settings with a new resolver instance, giving it the mapping dictionary you just constructed, and then pass the settings to JsonConvert.DeserializeObject().
var settings = new JsonSerializerSettings
{
ContractResolver = new DynamicMappingResolver(map)
};
var root = JsonConvert.DeserializeObject<RootObject>(json, settings);
Here is a demo: https://dotnetfiddle.net/ULkB0J
Why do this in one step? Why not deserialize into your standard object and then map them over dynamically using Automapper?
something like:
Mapper.Initialize(c =>
{
c.ReplaceMemberName("property1 ", "differen_property_name1");
});
If you don't want to use a custom ContractResolver to do this. Use [JsonProperty("")] to look for different variations of the property name and return with another property like this:
public class Details
{
private string _property1;
private string _property2;
[JsonProperty("property1")]
public string prop1 {get;set;}
[JsonProperty("foo")]
public string foo {get;set;}
public string getProperty1
{
get {_property1=prop1??foo;return _property1;}
set{prop1=value;foo=value;}
}
[JsonProperty("property2")]
public string prop2 {get;set;}
[JsonProperty("bar")]
public string bar {get;set;}
public string getProperty2
{
get {_property2=prop2??bar;return _property2;}
set {prop2=value;bar=value;}
}
}
Demo here: https://dotnetfiddle.net/V17igc
I don't believe that JSON.net has any support for what you are looking for. You should instead deserialize the JSON into JObject if you don't know the format or some generic one if you do (for instance if the JSON always says property1 you can use a generic object to represent it).
Once you have your generic object then next you need to translate the fields. Any that aren't changeable can be done directly, but for anything else you will need to use Reflection.
Basically it involves getting the type (typeof(Details) or obj.GetType()) and then searching for the Property you want to update. Finally you should be able to find the setter method and call it supplying the original value out of your generic object.
We are using ASP.NET Web API 2 and want to expose ability to partially edit some object in the following fashion:
HTTP PATCH /customers/1
{
"firstName": "John",
"lastName": null
}
... to set firstName to "John" and lastName to null.
HTTP PATCH /customers/1
{
"firstName": "John"
}
... in order just to update firstName to "John" and do not touch lastName at all. Suppose we have a lot of properties that we want to update with such semantic.
This is quite convenient behavior that is exercised by OData for instance.
The problem is that default JSON serializer will just come up with null in both cases, so it's impossible to distinguish.
I'm looking for some way to annotate model with some kind of wrappers (with value and flag set/unset inside) that would allow to see this difference. Any existing solutions for this?
I know that answers which are already given cover all aspects already, but just want to share concise summary of what we ended up doing and what seems to work for us pretty well.
Created a generic data contract
[DataContract]
public class RQFieldPatch<T>
{
[DataMember(Name = "value")]
public T Value { get; set; }
}
Created ad-hoc data cotnracts for patch requests
Sample is below.
[DataContract]
public class PatchSomethingRequest
{
[DataMember(Name = "prop1")]
public RQFieldPatch<EnumTypeHere> Prop1 { get; set; }
[DataMember(Name = "prop2")]
public RQFieldPatch<ComplexTypeContractHere> Prop2 { get; set; }
[DataMember(Name = "prop3")]
public RQFieldPatch<string> Prop3 { get; set; }
[DataMember(Name = "prop4")]
public RQFieldPatch<int> Prop4 { get; set; }
[DataMember(Name = "prop5")]
public RQFieldPatch<int?> Prop5 { get; set; }
}
Business Logic
Simple.
if (request.Prop1 != null)
{
// update code for Prop1, the value is stored in request.Prop1.Value
}
Json format
Simple. Not that extensive as "JSON Patch" standard, but covers all our needs.
{
"prop1": null, // will be skipped
// "prop2": null // skipped props also skipped as they will get default (null) value
"prop3": { "value": "test" } // value update requested
}
Properties
Simple contracts, simple logic
No serialization customization
Support for null values assignment
Covers any types: value, reference, complex custom types, whatever
At first I misunderstood the problem. As I was working with Xml I thought it was quite easy. Just add an attribute to the property and leave the property empty. But as I found out, Json doesn't work like that. Since I was looking for a solution that works for both xml and json, you'll find xml references in this answer. Another thing, I wrote this with a C# client in mind.
The first step is to create two classes for serialization.
public class ChangeType
{
[JsonProperty("#text")]
[XmlText]
public string Text { get; set; }
}
public class GenericChangeType<T> : ChangeType
{
}
I've chosen for a generic and a non-generic class because it is hard to cast to a generic type while this is not important. Also, for xml implementation it is necessary that XmlText is string.
XmlText is the actual value of the property. The advantage is that you can add attributes to this object and the fact that this is an object, not just string. In Xml it looks like: <Firstname>John</Firstname>
For Json this doesn't work. Json doesn't know attributes. So for Json this is just a class with properties. To implement the idea of the xml value (I will get to that later), I've renamed the property to #text. This is just a convention.
As XmlText is string (and we want to serialize to string), this is fine to store the value disregard the type. But in case of serialization, I want to know the actual type.
The drawback is that the viewmodel needs to reference these types, the advantage is that the properties are strongly typed for serialization:
public class CustomerViewModel
{
public GenericChangeType<int> Id { get; set; }
public ChangeType Firstname { get; set; }
public ChangeType Lastname { get; set; }
public ChangeType Reference { get; set; }
}
Suppose I set the values:
var customerViewModel = new CustomerViewModel
{
// Where int needs to be saved as string.
Id = new GenericeChangeType<int> { Text = "12" },
Firstname = new ChangeType { Text = "John" },
Lastname = new ChangeType { },
Reference = null // May also be omitted.
}
In xml this will look like:
<CustomerViewModel>
<Id>12</Id>
<Firstname>John</Firstname>
<Lastname />
</CustomerViewModel>
Which is enough for the server to detect the changes. But with json it will generate the following:
{
"id": { "#text": "12" },
"firstname": { "#text": "John" },
"lastname": { "#text": null }
}
It can work, because in my implementation the receiving viewmodel has the same definition. But since you are talking about serialization only and in case you use another implementation you would want:
{
"id": 12,
"firstname": "John",
"lastname": null
}
That is where we need to add a custom json converter to produce this result. The relevant code is in WriteJson, assuming you would add this converter to the serializer settings only. But for the sake of completeness I've added the readJson code as well.
public class ChangeTypeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// This is important, we can use this converter for ChangeType only
return typeof(ChangeType).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var value = JToken.Load(reader);
// Types match, it can be deserialized without problems.
if (value.Type == JTokenType.Object)
return JsonConvert.DeserializeObject(value.ToString(), objectType);
// Convert to ChangeType and set the value, if not null:
var t = (ChangeType)Activator.CreateInstance(objectType);
if (value.Type != JTokenType.Null)
t.Text = value.ToString();
return t;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var d = value.GetType();
if (typeof(ChangeType).IsAssignableFrom(d))
{
var changeObject = (ChangeType)value;
// e.g. GenericChangeType<int>
if (value.GetType().IsGenericType)
{
try
{
// type - int
var type = value.GetType().GetGenericArguments()[0];
var c = Convert.ChangeType(changeObject.Text, type);
// write the int value
writer.WriteValue(c);
}
catch
{
// Ignore the exception, just write null.
writer.WriteNull();
}
}
else
{
// ChangeType object. Write the inner string (like xmlText value)
writer.WriteValue(changeObject.Text);
}
// Done writing.
return;
}
// Another object that is derived from ChangeType.
// Do not add the current converter here because this will result in a loop.
var s = new JsonSerializer
{
NullValueHandling = serializer.NullValueHandling,
DefaultValueHandling = serializer.DefaultValueHandling,
ContractResolver = serializer.ContractResolver
};
JToken.FromObject(value, s).WriteTo(writer);
}
}
At first I tried to add the converter to the class: [JsonConverter(ChangeTypeConverter)]. But the problem is that the converter will be used at all times, which creates a reference loop (as also mentioned in the comment in the code above). Also you may want to use this converter for serialization only. That is why I've added it to the serializer only:
var serializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
Converters = new List<JsonConverter> { new ChangeTypeConverter() },
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};
var s = JsonConvert.SerializeObject(customerViewModel, serializerSettings);
This will generate the json I was looking for and should be enough to let the server detect the changes.
-- update --
As this answer focusses on serialization, the most important thing is that lastname is part of the serialization string. It then depends on the receiving party how to deserialize the string into an object again.
Serialization and deserialization use different settings. In order to deserialize again you can use:
var deserializerSettings = new JsonSerializerSettings
{
//NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
Converters = new List<JsonConverter> { new Converters.NoChangeTypeConverter() },
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};
var obj = JsonConvert.DeserializeObject<CustomerViewModel>(s, deserializerSettings);
If you use the same classes for deserialization then Request.Lastname should be of ChangeType, with Text = null.
I'm not sure why removing the NullValueHandling from the deserialization settings causes problems in your case. But you can overcome this by writing an empty object as value instead of null. In the converter the current ReadJson can already handle this. But in WriteJson there has to be a modification. Instead of writer.WriteValue(changeObject.Text); you need something like:
if (changeObject.Text == null)
JToken.FromObject(new ChangeType(), s).WriteTo(writer);
else
writer.WriteValue(changeObject.Text);
This would result in:
{
"id": 12,
"firstname": "John",
"lastname": {}
}
Here's my quick and inexpensive solution...
public static ObjectType Patch<ObjectType>(ObjectType source, JObject document)
where ObjectType : class
{
JsonSerializerSettings settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
try
{
String currentEntry = JsonConvert.SerializeObject(source, settings);
JObject currentObj = JObject.Parse(currentEntry);
foreach (KeyValuePair<String, JToken> property in document)
{
currentObj[property.Key] = property.Value;
}
String updatedObj = currentObj.ToString();
return JsonConvert.DeserializeObject<ObjectType>(updatedObj);
}
catch (Exception ex)
{
throw ex;
}
}
When fetching the request body from your PATCH based method, make sure to take the argument as a type such as JObject. JObject during iteration returns a KeyValuePair struct which inherently simplifies the modification process. This allows you to get your request body content without receiving a deserialized result of your desired type.
This is beneficial due to the fact that you don't need any additional verification for nullified properties. If you want your values to be nullified that also works because the Patch<ObjectType>() method only loops through properties given in the partial JSON document.
With the Patch<ObjectType>() method, you only need to pass your source or target instance, and the partial JSON document that will update your object. This method will apply camelCase based contract resolver to prevent incompatible and inaccurate property names from being made. This method will then serialize your passed instance of a certain type and turned into a JObject.
The method then replaces all properties from the new JSON document to the current and serialized document without any unnecessary if statements.
The method stringifies the current document which now is modified, and deserializes the modified JSON document to your desired generic type.
If an exception occur, the method will simply throw it. Yes, it is rather unspecific but you are the programmer, you need to know what to expect...
This can all be done on a single and simple syntax with the following:
Entity entity = AtomicModifier.Patch<Entity>(entity, partialDocument);
This is what the operation would normally look like:
// Partial JSON document (originates from controller).
JObject newData = new { role = 9001 };
// Current entity from EF persistence medium.
User user = await context.Users.FindAsync(id);
// Output:
//
// Username : engineer-186f
// Role : 1
//
Debug.WriteLine($"Username : {0}", user.Username);
Debug.WriteLine($"Role : {0}", user.Role);
// Partially updated entity.
user = AtomicModifier.Patch<User>(user, newData);
// Output:
//
// Username : engineer-186f
// Role : 9001
//
Debug.WriteLine($"Username : {0}", user.Username);
Debug.WriteLine($"Role : {0}", user.Role);
// Setting the new values to the context.
context.Entry(user).State = EntityState.Modified;
This method will work well if you can correctly map your two documents with the camelCase contract resolver.
Enjoy...
Update
I updated the Patch<T>() method with the following code...
public static T PatchObject<T>(T source, JObject document) where T : class
{
Type type = typeof(T);
IDictionary<String, Object> dict =
type
.GetProperties()
.ToDictionary(e => e.Name, e => e.GetValue(source));
string json = document.ToString();
var patchedObject = JsonConvert.DeserializeObject<T>(json);
foreach (KeyValuePair<String, Object> pair in dict)
{
foreach (KeyValuePair<String, JToken> node in document)
{
string propertyName = char.ToUpper(node.Key[0]) +
node.Key.Substring(1);
if (propertyName == pair.Key)
{
PropertyInfo property = type.GetProperty(propertyName);
property.SetValue(source, property.GetValue(patchedObject));
break;
}
}
}
return source;
}
I know I'm a little bit late on this answer, but I think I have a solution that doesn't require having to change serialization and also doesn't include reflection (This article refers you to a JsonPatch library that someone wrote that uses reflection).
Basically create a generic class representing a property that could be patched
public class PatchProperty<T> where T : class
{
public bool Include { get; set; }
public T Value { get; set; }
}
And then create models representing the objects that you want to patch where each of the properties is a PatchProperty
public class CustomerPatchModel
{
public PatchProperty<string> FirstName { get; set; }
public PatchProperty<string> LastName { get; set; }
public PatchProperty<int> IntProperty { get; set; }
}
Then your WebApi method would look like
public void PatchCustomer(CustomerPatchModel customerPatchModel)
{
if (customerPatchModel.FirstName?.Include == true)
{
// update first name
string firstName = customerPatchModel.FirstName.Value;
}
if (customerPatchModel.LastName?.Include == true)
{
// update last name
string lastName = customerPatchModel.LastName.Value;
}
if (customerPatchModel.IntProperty?.Include == true)
{
// update int property
int intProperty = customerPatchModel.IntProperty.Value;
}
}
And you could send a request with some Json that looks like
{
"LastName": { "Include": true, "Value": null },
"OtherProperty": { "Include": true, "Value": 7 }
}
Then we would know to ignore FirstName but still set the other properties to null and 7 respectively.
Note that I haven't tested this and I'm not 100% sure it would work. It would basically rely on .NET's ability to serialize the generic PatchProperty. But since the properties on the model specify the type of the generic T, I would think it would be able to. Also since we have "where T : class" on the PatchProperty declaration, the Value should be nullable. I'd be interested to know if this actually works though. Worst case you could implement a StringPatchProperty, IntPatchProperty, etc. for all your property types.
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
}
}