The following class does not deserialize (but does serialize) using System.Web.Script.Serialization.JavaScriptSerializer.
public class foo {
public KeyValuePair<string, string>? bar {get;set;}
}
The attempt to deserialize results in a System.NullReferenceException when System.Web.Script.Serialization.ObjectConverter.ConvertDictionaryToObject reaches the bar property. (Note, that is a surmise based on the stack trace.)
Changing the property type to KeyValuePair<string,string> fixes the problem, but I'd like to keep the Nullable type if at all possible.
The JSON is exactly what you would expect:
{"foo": {
"bar": {
"Key":"Jean-Luc",
"Value":"Picard"
}
}}
Help?
The reason this happens is that when the JavaScriptSerializer tries to deserialize it will create a new instance of the class (in this the KeyValuePair) and then assign the values to properties.
This causes an issue as the KeyValuePair can only have the key and values assigned as part of the constructor and not via properties so results in an empty key and value.
You will be able to resolve this and the null issue by creating a class that implements JavaScriptConverter and Registering It. I have used the code below to handle a standard KeyValuePair but I am sure you can extend it to handle nulls.
public class DictionaryJavaScriptConverter<k, v> : JavaScriptConverter
{
public override object Deserialize(System.Collections.Generic.IDictionary<string, object> dictionary, System.Type type, System.Web.Script.Serialization.JavaScriptSerializer serializer)
{
return new KeyValuePair<k, v>((k)dictionary["Key"], (v)dictionary["Value"]);
}
public override System.Collections.Generic.IDictionary<string, object> Serialize(object obj, System.Web.Script.Serialization.JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override System.Collections.Generic.IEnumerable<System.Type> SupportedTypes {
get { return new System.Type[] { typeof(KeyValuePair<k, v>) }; }
}
}
Alternately you can create a simple class that has two properties key and value.
You can have a look at this wrapper:
http://www.codeproject.com/KB/aspnet/Univar.aspx
I've successfully Json serialized and deserialized the nullable KeyValue pair using it.
Related
In order to avoid losing JSON properties when deserializating to a POCO that is missing members, I use the [JsonExtensionData] attribute. Ex:
public class Foo
{
public int Y { get; set; }
[JsonExtensionData]
private IDictionary<string, JToken> _extraStuff;
}
That way, if I try to deserialize the following, I won't lose the z property:
{
"y": 1,
"z": "added in foo"
}
So far so good.
But in reality, I have a really deep object graph. So every POCO in the graph must use the [JsonExtensionData] attribute. This is a little dangerous. As soon as I forget to add this in one of the class, doing a deserialization followed by a serialization will lose data. (the real use case is doing a HTTP GET followed by a HTTP POST and I want to be sure that I don't lose anything)
So, to be sure that I haven't forgotten any [JsonExtensionData] in my whole POCO object tree, I thought about using the following deserializer setting:
var serializerSettings = new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Error
};
But then, if I try to deserialize the previous JSON, I get the following exception:
Could not find member 'z' on object of type 'Foo'. Path 'z', line 3, position 6.
This is a quite annoying, it complains about a field that has no member in the POCO but that is covered by the [JsonExtensionData] attribute.
Is there a way to only raise errors when data is actually data being lost during the deserialization?
You may mark your object with [JsonObject(MissingMemberHandling = MissingMemberHandling.Ignore)]. This will override the serializer setting:
[JsonObject(MissingMemberHandling = MissingMemberHandling.Ignore)]
public class Foo
{
public int Y { get; set; }
[JsonExtensionData]
private IDictionary<string, JToken> _extraStuff;
}
Demo fiddle #1 here.
Alternatively, you could create a custom contract resolver that does this automatically:
public class MissingMemberContractResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
if (contract.ExtensionDataSetter != null && contract.MissingMemberHandling == null)
{
contract.MissingMemberHandling = MissingMemberHandling.Ignore;
}
return contract;
}
}
Then use it as follow. First cache a copy somewhere for performance:
static IContractResolver contractResolver = new MissingMemberContractResolver
{
// Modify settings such as the naming strategy if required.
NamingStrategy = new CamelCaseNamingStrategy(),
};
And then set in settings as follows:
var serializerSettings = new JsonSerializerSettings
{
ContractResolver = contractResolver,
MissingMemberHandling = MissingMemberHandling.Error
};
var foo = JsonConvert.DeserializeObject<Foo>(json, serializerSettings);
var json2 = JsonConvert.SerializeObject(foo, Formatting.Indented, serializerSettings);
Demo fiddle #2 here.
Note that MissingMemberHandling was added to to JsonObjectAttribute and JsonObjectContract in Json.NET release 12.0.2. On earlier versions neither of the above solutions are available.
Honestly I'm a bit surprised this is necessary.
As an aside, if you're creating a custom contract resolver anyway, you could make DefaultContractResolver.CreateObjectContract() throw for any object in your assembly or namespace that lacks an ExtensionDataGetter and ExtensionDataSetter. If you do that you'll be able to discover any types that lack a [JsonExtensionData] during unit testing.
I've RESTful service Spring MVC based.
The service has a RESTful resource method that returns the following response:
public class OperationalDataResponse<T> {
private String status;
private String statusMessage;
private T result;
//getters and setters
}
This response object encapsulates the result object of type T.
On the client side I use RestTemplate with GsonHttpMessageConverter added.
I get the response from service as a ResponseEntity
I handle the generic response with runtime Type as below:
public class OperationalDataRestClient<REQ,RESULT_TYPE> {
public OperationalDataResponse<RESULT_TYPE> getOperationalData(String resourcePath, Map<String, Object> urlVariables, Class<RESULT_TYPE> resultType) {
//code to invoke service and get data goes here
String responseString = responseEntity.getBody();
response = GsonHelper.getInstance().fromJson(responseString, getType(OperationalDataResponse.class, resultType));
}
Type getType(final Class<?> rawClass, final Class<?> parameter) {
return new ParameterizedType() {
#Override
public Type[] getActualTypeArguments() {
return new Type[] { parameter };
}
#Override
public Type getRawType() {
return rawClass;
}
#Override
public Type getOwnerType() {
return null;
}
};
}
}
This works like a charm as long as my resultType is a non-collection class.
So, this works great from caller code:
getOperationalData(someResourcePath, someUrlVariables, MyNonGenericClass.class)
However if my resultType is a collection (say, List<String> or List<MyNonGenericClass>)
then I don't know how to pass the resultType Class from the caller code.
For example, from caller code,
getOperationalData(someResourcePath, someUrlVariables, List.class)
or
getOperationalData(someResourcePath, someUrlVariables, List<MyNonGenericClass>.class)
throws compilation error.
I tried passing on ArrayList.class as well but that too doesn't work.
Any suggestion how can I pass a generic collection as a resultType from caller code (in other words, as an example, how can I pass the class object of a List<String> or List<MyNonGenericClass> from caller code ?)
If you know that ResultType is coming as a List, Then it will obvious fail like you said compilation issue.Why? because you are trying to send a List when you method only accepts a single value.In order to over come that issue you will have to change the method arguments to the following
public OperationalDataResponse<RESULT_TYPE> getOperationalData(String resourcePath, Map<String, Object> urlVariables, List<Class<RESULT_TYPE>> resultType){
....
}
and you will have to make some slight modification to getType() Method,loop it and then pass each class value to getType method like so
for(MyNonGenericClass myClass:mylist){
getType(OperationalDataResponse.class, myClass.getClass());
}
I have a controller that accepts a model UpdateProductCommand like this:
public IHttpActionResult UpdateProduct(UpdateProductCommand command)
{
command.AuditUserName = this.RequestContext.Principal.Identity.Name;
// ....
}
For security issues, the AuditUserName field should never be set outside (from the API call).
How can I remove (or truncate) the value of this field from JSON request?
It can be achieved by a following ModelBinder:
using Newtonsoft.Json.Linq;
public class FieldRemoverModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
string content = actionContext.Request.Content.ReadAsStringAsync().Result;
JObject json = JObject.Parse(content);
JToken property = json.GetValue(bindingContext.ModelName, StringComparison.OrdinalIgnoreCase);
property?.Parent.Remove();
bindingContext.Model = json.ToObject(bindingContext.ModelType);
return true;
}
}
Use it like this:
public IHttpActionResult UpdateProduct(([ModelBinder(typeof(FieldRemoverModelBinder), Name = nameof(UpdateProductCommand.AuditUserName))]UpdateProductCommand command)
{
// here command.AuditUserName will always be empty, no matter what's in json
That's what DTOs are for.
You can just create another class (UpdateProductCommandDto for example) that has only the properties you need / want to be used as the input, and then you can just use something like Automapper to map it to a new instance of UpdateProductCommand.
Given the following class:
[DataContract]
public class Enumerables
{
[DataMember]
public IEnumerable<Byte> ByteMember { get; set; }
}
And an instance initialized as:
var bytes = new byte[] { ... };
var o = new Enumerables { ByteMember = bytes };
Serialization produces this:
{"ByteMember": "<<base-64-encoded-string>>"}
But this string cannot be deserialized. The error produced is:
Newtonsoft.Json.JsonSerializationException : Error converting value
"vbMBTToz9gyZj6gZuA59rE7ryu3fCfimjVMn8R6A0277Xs9u" to
type 'System.Collections.Generic.IEnumerable`1[System.Byte]'.
Path 'ByteMember', line 1, position 8084.
----> System.ArgumentException : Could not cast or convert from
System.String to System.Collections.Generic.IEnumerable`1[System.Byte].
I don't see this happening for byte[], List<byte> or Collection<byte> properties, which are correctly serialized to and from base-64 strings. And I don't see this happening for any IEnumerable<T> where T is not a byte -- for example, a property of type IEnumerable<int> deserializes to a List<double>, an effective implementation.
How an IEnumerable<byte> gets serialized depends on the concrete type that is assigned to it. If the concrete type is a byte[] then it will get serialized specially as a base-64 encoded string, whereas if it is some other concrete type like a List<byte>, it will be serialized as a normal array of numbers. The same is true of ICollection<byte> and IList<byte>. (DEMO)
On deserialization, Json.Net looks at the types of the member properties of the target class to determine what types of objects to create. When the member property is a concrete type, no problem; it creates an instance of that type and tries to populate it from the JSON. If the member type is an interface, then Json.Net has to make a guess, or throw an error. You could argue that Json.Net should be smart enough to guess that if the member variable is an IEnumerable<byte> and the JSON value is a base-64 encoded string, it should convert the string to a byte[]. But that is not how it is implemented. In fact, the special handling for base-64 encoded byte arrays is only triggered if the member property is byte[] specifically. With no special handling for IEnumerable<byte>, this results in an error because a string can't be assigned directly to an IEnumerable<byte>. Again, the same is true for ICollection<byte> or IList<byte>.
(DEMO)
If you want it to work the same for types implementing IEnumerable<byte> as it does for byte[], you can make a custom JsonConveter like this:
public class EnumerableByteConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(IEnumerable<byte>).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
byte[] bytes = ((IEnumerable<byte>)value).ToArray();
writer.WriteValue(Convert.ToBase64String(bytes));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
byte[] bytes = Convert.FromBase64String(reader.Value.ToString());
if (objectType == typeof(byte[]))
{
return bytes;
}
return new List<byte>(bytes);
}
}
To use the converter, create an instance of JsonSerializerSettings and add an instance of the converter to the Converters collection. Then, pass the settings to SerializerObject() and DeserializeObject() methods. For example:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new EnumerableByteConverter());
string json = JsonConvert.SerializeObject(obj, settings);
Here is a working round-trip demo. Note, however, that this converter doesn't (and can't) handle every possible IEnumerable<byte> that might be out there. For example, it won't work with ISet<byte> as currently implemented. If you need support for this or other additional types, you will need to extend the ReadJson method to handle that. I leave this to you.
I have written a nifty function that will accept a system.object, reflect on its properties and serialize the object into a JSON string. It looks like this:
public class JSONSerializer
{
public string Serialize(object obj)
Now, I want to be able to do this to serialize a dynamic/ExpandoObject, but because my serializer uses reflection, it isn't able to do it. What's the workaround?
public class Test
{
public dynamic MakeDynamicCat()
{
dynamic newCat = new ExpandoObject();
newCat.Name = "Polly";
newCat.Pedigree = new ExpandoObject();
newCat.Pedigree.Breed = "Whatever";
return newCat;
}
public void SerializeCat()
{
new JSONSerializer().Serialize(MakeDynamicCat());
}
}
I think, this question is very similar: How do I reflect over the members of dynamic object?
At least the answers should help you too.