How to deserialize a child list of interfaces? - json

I'm using ASP.NET MVC2 and I have the following object structure:
public class IDealer {
string Name { get; set; }
List<IVehicle> Vehicles { get; set; }
}
public class DealerImpl {
public string Name { get; set; }
public List<IVehicle> Vehicles { get; set; }
}
public interface IVehicle {
string Type { get; }
}
public class Car : IVehicle {
public string Type { get { return this.GetType().FullName; } }
}
public class Truck : IVehicle {
public string Type { get { return this.GetType().FullName; } }
}
I have the following class as my ModelBinder which deserializes objects in my page requests:
public class JsonModelBinder : DefaultModelBinder {
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
return deserialize(controllerContext, bindingContext);
}
protected static bool IsJSONRequest(ControllerContext controllerContext) {
var contentType = controllerContext.HttpContext.Request.ContentType;
return contentType.Contains("application/json");
}
protected virtual object deserialize(ControllerContext controllerContext, ModelBindingContext bindingContext) {
Type modelType = bindingContext.ModelMetadata.ModelType;
bool isNotConcrete = bindingContext.ModelMetadata.ModelType.IsInterface || bindingContext.ModelMetadata.ModelType.IsAbstract;
if (!IsJSONRequest(controllerContext)) {
return base.BindModel(controllerContext, bindingContext);
} else {
var request = controllerContext.HttpContext.Request;
var jsonStringData = new StreamReader(request.InputStream).ReadToEnd();
if (isNotConcrete) {
Dictionary<string, Object> result = JsonConvert.DeserializeObject<Dictionary<string, Object>>(jsonStringData);
string type = result["Type"] as string;
modelType = Type.GetType(type + ",MyCompany.Common");
}
return JsonConvert.DeserializeObject(jsonStringData, modelType, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
}
}
}
// ASP.NET MVC Controller
protected override void Initialize(System.Web.Routing.RequestContext requestContext) {
base.Initialize(requestContext);
ModelBinders.Binders.DefaultBinder = new JsonModelBinder();
}
[HttpPost]
public ActionResult addUpdateDealer(IDealer dealer) {
// breaks before here with the error in the comment below
}
// and in the aspx page
<script>
var model = <%= JsonConvert.SerializeObject(Model, Formatting.None, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }) %>;
</script>
The problem I'm running into, is that when the code tries to deserialize the child list of IVehicles, it does not know which type of vehicle to instantiate. I put a property on IVehicle called "Type" which could be used to help determine which class to instantiate, but I'm not sure what/where/how to provide an override to perform this check.

Your solution is similar to what JSON.NET has built-in now, called TypeNameHandling. Here are the release notes on that.
Your JSON message will need to include a $type property, which won't be deserialized, but will be interpreted by the deserializer as the concrete type to use.

Related

Is it possible to tell Newtonsoft to map a declared $type to a different type when deserialising? [duplicate]

Suppose I have the following class hierarchy:
public abstract class Organization
{
/* properties related to all organizations */
}
public sealed class Company : Organization
{
/* properties related to companies */
}
public sealed class NonProfitOrganization : Organization
{
/* properties related to non profit organizations */
}
Is it possible to have json.net use property (say "type" or "discriminator") to determine which type the object when it deserializes the organization? For example, the following should deserialize an instance of Company.
{
"type": "company"
/* other properties related to companies */
}
And the following should deserialize an instance of NonProfitOrganization.
{
"type": "non-profit"
/* other properties related to non profit */
}
When I call the following:
Organization organization = JsonConvert.DeserializeObject<Organization>(payload);
where payload is the above JSON snippets. I had a look at setting the "TypeNameHandling" on properties or classes but it serializes the whole .NET type, which isn't "portable" between the client and server when the classes are defined in different namespaces and assemblies.
I'd rather define the type is a neutral manner which clients written in any language can use to determine the actual type of the object type being serialized.
In case you are still looking, here is an example: http://james.newtonking.com/archive/2011/11/19/json-net-4-0-release-4-bug-fixes.aspx
This will allow you to create a table based mapping:
public class TypeNameSerializationBinder : SerializationBinder
{
public TypeNameSerializationBinder(Dictionary<Type, string> typeNames = null)
{
if (typeNames != null)
{
foreach (var typeName in typeNames)
{
Map(typeName.Key, typeName.Value);
}
}
}
readonly Dictionary<Type, string> typeToName = new Dictionary<Type, string>();
readonly Dictionary<string, Type> nameToType = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
public void Map(Type type, string name)
{
this.typeToName.Add(type, name);
this.nameToType.Add(name, type);
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
var name = typeToName.Get(serializedType);
if (name != null)
{
assemblyName = null;
typeName = name;
}
else
{
assemblyName = serializedType.Assembly.FullName;
typeName = serializedType.FullName;
}
}
public override Type BindToType(string assemblyName, string typeName)
{
if (assemblyName == null)
{
var type = this.nameToType.Get(typeName);
if (type != null)
{
return type;
}
}
return Type.GetType(string.Format("{0}, {1}", typeName, assemblyName), true);
}
}
The code has a slight defect in that if a type name mapping is attempted where the type is unique but the name is already used, the Map method will throw an exception after the type-to-name mapping is already added leaving the table in an inconsistent state.
To take eulerfx's answer further; I wanted to apply DisplayName attribute to a class and have that automatically become the type name used; to that end:
public class DisplayNameSerializationBinder : DefaultSerializationBinder
{
private Dictionary<string, Type> _nameToType;
private Dictionary<Type, string> _typeToName;
public DisplayNameSerializationBinder()
{
var customDisplayNameTypes =
this.GetType()
.Assembly
//concat with references if desired
.GetTypes()
.Where(x => x
.GetCustomAttributes(false)
.Any(y => y is DisplayNameAttribute));
_nameToType = customDisplayNameTypes.ToDictionary(
t => t.GetCustomAttributes(false).OfType<DisplayNameAttribute>().First().DisplayName,
t => t);
_typeToName = _nameToType.ToDictionary(
t => t.Value,
t => t.Key);
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
if (false == _typeToName.ContainsKey(serializedType))
{
base.BindToName(serializedType, out assemblyName, out typeName);
return;
}
var name = _typeToName[serializedType];
assemblyName = null;
typeName = name;
}
public override Type BindToType(string assemblyName, string typeName)
{
if (_nameToType.ContainsKey(typeName))
return _nameToType[typeName];
return base.BindToType(assemblyName, typeName);
}
}
and usage example:
public class Parameter
{
public string Name { get; set; }
};
[DisplayName("bool")]
public class BooleanParameter : Parameter
{
}
[DisplayName("string")]
public class StringParameter : Parameter
{
public int MinLength { get; set; }
public int MaxLength { get; set; }
}
[DisplayName("number")]
public class NumberParameter : Parameter
{
public double Min { get; set; }
public double Max { get; set; }
public string Unit { get; set; }
}
[DisplayName("enum")]
public class EnumParameter : Parameter
{
public string[] Values { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var parameters = new Parameter[]
{
new BooleanParameter() {Name = "alive"},
new StringParameter() {Name = "name", MinLength = 0, MaxLength = 10},
new NumberParameter() {Name = "age", Min = 0, Max = 120},
new EnumParameter() {Name = "status", Values = new[] {"Single", "Married"}}
};
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Binder = new DisplayNameSerializationBinder(),
TypeNameHandling = TypeNameHandling.Auto,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
Formatting = Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var json = JsonConvert.SerializeObject(parameters);
var loadedParams = JsonConvert.DeserializeObject<Parameter[]>(json);
Console.WriteLine(JsonConvert.SerializeObject(loadedParams));
}
}
output:
[
{
"$type": "bool",
"name": "alive"
},
{
"$type": "string",
"maxLength": 10,
"name": "name"
},
{
"$type": "number",
"max": 120.0,
"name": "age"
},
{
"$type": "enum",
"values": [
"Single",
"Married"
],
"name": "status"
}
]
I've written purely declarative solution with ability to specify custom discriminator field, and provide scoped name handling per base class (as opposed to usecure global JsonSerializationSettings, especially on different Web-Api when we do not have ability to specify custom JsonSerializationSettings).
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;
// Discriminated Json Converter (JsonSubtypes) implementation for .NET
//
// MIT License
//
// Copyright (c) 2016 Anatoly Ressin
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
////////////////////// USAGE ////////////////////////////////////////////////////////////////////////////////
[JsonConverter(typeof(JsonSubtypes))] // Discriminated base class SHOULD NOT be abstract
public class ShapeBase {
[JsonTag, JsonProperty("#type")] // it SHOULD contain a property marked with [JsonTag]
public string Type {get;set;} // only one [JsonTag] annotation allowed per discriminated class
// it COULD contain other properties, however this is NOT RECOMMENDED
// Rationale: instances of this class will be created at deserialization
// only for tag sniffing, and then thrown away.
}
public abstract class Shape: ShapeBase { // If you want abstract parent - extend the root
public abstract double GetArea(); // with needed abstract stuff, then use this class everywhere (see DEMO below)
}
[JsonSubtype("circle")] // Every final class-case SHOULD be marked with [JsonSubtype(tagValue)]
public class Circle: Shape { // Two disctinct variant classes MUST have distinct tagValues
[JsonProperty("super-radius")] // You CAN use any Json-related annotation as well
public double Radius { get; set; }
public override double GetArea() {
return Radius * Radius * Math.PI;
}
}
[JsonSubtype("rectangle")]
public class Rectangle: Shape {
public double Height { get; set; }
public double Width { get; set; }
public override double GetArea() {
return Width * Height;
}
}
[JsonSubtype("group")]
public class Group: Shape {
[JsonProperty("shapes")]
public List<Shape> Items { get; set; }
public override double GetArea() {
return Items.Select(item => item.GetArea()).Sum();
}
}
// Every final class-case SHOULD be registered with JsonSubtypes.register(typeof(YourConcreteClass))
// either manually or with auto-register capability:
// You can auto-register all classes marked with [JsonSubtype(tag)] in given Assembly
// using JsonSubtypes.autoRegister(yourAssembly)
////////////////// DEMO /////////////////////////////////////////////////////////////////////////////////
public class Program
{
public static void Main()
{
JsonSubtypes.autoRegister(Assembly.GetExecutingAssembly());
Shape original = new Group() {
Items = new List<Shape> {
new Circle() { Radius = 5 },
new Rectangle() { Height = 10, Width = 20 }
}
};
string str = JsonConvert.SerializeObject(original);
Console.WriteLine(str);
var copy = JsonConvert.DeserializeObject(str,typeof(Shape)) as Shape;
// Note: we can deserialize object using any class from the hierarchy.
// Under the hood, anyway, it will be deserialized using the top-most
// base class annotated with [JsonConverter(typeof(JsonSubtypes))].
// Thus, only soft-casts ("as"-style) are safe here.
Console.WriteLine("original.area = {0}, copy.area = {1}", original.GetArea(), copy.GetArea());
}
}
//////////////////////// IMPLEMENTATION //////////////////////////////////////////////////////////////////
public class JsonSubtypeClashException: Exception {
public string TagValue { get; private set;}
public Type RootType { get; private set; }
public Type OldType { get; private set; }
public Type NewType { get; private set; }
public JsonSubtypeClashException(Type rootType, string tagValue, Type oldType, Type newType): base(
String.Format(
"JsonSubtype Clash for {0}[tag={1}]: oldType = {2}, newType = {3}",
rootType.FullName,
tagValue,
oldType.FullName,
newType.FullName
)
) {
TagValue = tagValue;
RootType = rootType;
OldType = oldType;
NewType = newType;
}
}
public class JsonSubtypeNoRootException: Exception {
public Type SubType { get; private set; }
public JsonSubtypeNoRootException(Type subType): base(
String.Format(
"{0} should be inherited from the class with the [JsonConverter(typeof(JsonSubtypes))] attribute",
subType.FullName
)
) {
SubType = subType;
}
}
public class JsonSubtypeNoTagException: Exception {
public Type SubType { get; private set; }
public JsonSubtypeNoTagException(Type subType): base(
String.Format(
#"{0} should have [JsonSubtype(""..."")] attribute",
subType.FullName
)
) {
SubType = subType;
}
}
public class JsonSubtypeNotRegisteredException: Exception {
public Type Root { get; private set; }
public string TagValue { get; private set; }
public JsonSubtypeNotRegisteredException(Type root, string tagValue): base(
String.Format(
#"Unknown tag={1} for class {0}",
root.FullName,
tagValue
)
) {
Root = root;
TagValue = tagValue;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class JsonSubtypeAttribute: Attribute {
private string tagValue;
public JsonSubtypeAttribute(string tagValue) {
this.tagValue = tagValue;
}
public string TagValue {
get {
return tagValue;
}
}
}
public static class JsonSubtypesExtension {
public static bool TryGetAttribute<T>(this Type t, out T attribute) where T: Attribute {
attribute = t.GetCustomAttributes(typeof(T), false).Cast<T>().FirstOrDefault();
return attribute != null;
}
private static Dictionary<Type, PropertyInfo> tagProperties = new Dictionary<Type, PropertyInfo>();
public static bool TryGetTagProperty(this Type t, out PropertyInfo tagProperty) {
if (!tagProperties.TryGetValue(t, out tagProperty)) {
JsonConverterAttribute conv;
if (t.TryGetAttribute(out conv) && conv.ConverterType == typeof(JsonSubtypes)) {
var props = (from prop in t.GetProperties() where prop.GetCustomAttribute(typeof(JsonTagAttribute)) != null select prop).ToArray();
if (props.Length == 0) throw new Exception("No tag");
if (props.Length > 1) throw new Exception("Multiple tags");
tagProperty = props[0];
} else {
tagProperty = null;
}
tagProperties[t] = tagProperty;
}
return tagProperty != null;
}
public static bool TryGetTagValue(this Type t, out string tagValue) {
JsonSubtypeAttribute subtype;
if (t.TryGetAttribute(out subtype)) {
tagValue = subtype.TagValue;
return true;
} else {
tagValue = null;
return false;
}
}
public static bool TryGetJsonRoot(this Type t, out Type root, out PropertyInfo tagProperty) {
root = t;
do {
if (root.TryGetTagProperty(out tagProperty)) {
return true;
}
root = root.BaseType;
} while (t != null);
return false;
}
}
public class JsonTagAttribute: Attribute {
}
public class JsonTagInfo {
public PropertyInfo Property { get; set; }
public string Value { get; set; }
}
public class JsonRootInfo {
public PropertyInfo Property { get; set; }
public Type Root { get; set; }
}
public abstract class DefaultJsonConverter: JsonConverter {
[ThreadStatic]
private static bool silentWrite;
[ThreadStatic]
private static bool silentRead;
public sealed override bool CanWrite {
get {
var canWrite = !silentWrite;
silentWrite = false;
return canWrite;
}
}
public sealed override bool CanRead {
get {
var canRead = !silentRead;
silentRead = false;
return canRead;
}
}
protected void _WriteJson(JsonWriter writer, Object value, JsonSerializer serializer) {
silentWrite = true;
serializer.Serialize(writer, value);
}
protected Object _ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) {
silentRead = true;
return serializer.Deserialize(reader, objectType);
}
}
public class JsonSubtypes: DefaultJsonConverter {
private static Dictionary<Type, Dictionary<string, Type>> implementations = new Dictionary<Type, Dictionary<string, Type>>();
private static Dictionary<Type, JsonTagInfo> tags = new Dictionary<Type, JsonTagInfo>();
private static Dictionary<Type, JsonRootInfo> roots = new Dictionary<Type, JsonRootInfo>();
public static void register(Type newType) {
PropertyInfo tagProperty;
Type root;
if (newType.TryGetJsonRoot(out root, out tagProperty)) {
for(var t = newType; t != root; t = t.BaseType) {
roots[t] = new JsonRootInfo() {
Property = tagProperty,
Root = root
};
}
roots[root] = new JsonRootInfo() {
Property = tagProperty,
Root = root
};
Dictionary<string, Type> implementationMap;
if (!implementations.TryGetValue(root, out implementationMap)) {
implementationMap = new Dictionary<string, Type>();
implementations[root] = implementationMap;
}
JsonSubtypeAttribute attr;
if (!newType.TryGetAttribute(out attr)) {
throw new JsonSubtypeNoTagException(newType);
}
var tagValue = attr.TagValue;
Type oldType;
if (implementationMap.TryGetValue(tagValue, out oldType)) {
throw new JsonSubtypeClashException(root, tagValue, oldType, newType);
}
implementationMap[tagValue] = newType;
tags[newType] = new JsonTagInfo() {
Property = tagProperty,
Value = tagValue
};
} else {
throw new JsonSubtypeNoRootException(newType);
}
}
public static void autoRegister(Assembly assembly) {
foreach(var type in assembly.GetTypes().Where(type => type.GetCustomAttribute<JsonSubtypeAttribute>() != null)) {
register(type);
}
}
public override bool CanConvert(Type t) {
return true;
}
public static T EnsureTag<T>(T value) {
JsonTagInfo tagInfo;
if (tags.TryGetValue(value.GetType(), out tagInfo)) {
tagInfo.Property.SetValue(value, tagInfo.Value);
}
return value;
}
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer) {
_WriteJson(writer, EnsureTag(value), serializer);
}
public override Object ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) {
JsonTagInfo tagInfo;
if (tags.TryGetValue(objectType, out tagInfo)) {
return _ReadJson(reader, objectType, existingValue, serializer);
} else {
JsonRootInfo rootInfo;
if (roots.TryGetValue(objectType, out rootInfo)) {
JToken t = JToken.ReadFrom(reader);
var stub = _ReadJson(t.CreateReader(), rootInfo.Root, existingValue, serializer);
var tagValue = rootInfo.Property.GetValue(stub) as string;
var implementationMap = implementations[rootInfo.Root];
Type implementation;
if (implementationMap.TryGetValue(tagValue, out implementation)) {
return ReadJson(t.CreateReader(), implementation, null, serializer);
} else {
throw new JsonSubtypeNotRegisteredException(rootInfo.Root, tagValue);
}
} else {
return _ReadJson(reader, objectType, existingValue, serializer);
}
}
}
public static T Deserialize<T>(string s) where T: class {
return JsonConvert.DeserializeObject(s, typeof(T)) as T;
}
public static string Serialize<T>(T value) where T: class {
return JsonConvert.SerializeObject(value);
}
}
output:
{"shapes":[{"super-radius":5.0,"#type":"circle"},{"Height":10.0,"Width":20.0,"#type":"rectangle"}],"#type":"group"}
original.area = 278.539816339745, copy.area = 278.539816339745
You can grab it here:
https://dotnetfiddle.net/ELcvnk
With another JsonSubtypes converter implementation.
Usage:
[JsonConverter(typeof(JsonSubtypes), "Sound")]
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
public class Animal
{
public virtual string Sound { get; }
public string Color { get; set; }
}
public class Dog : Animal
{
public override string Sound { get; } = "Bark";
public string Breed { get; set; }
}
public class Cat : Animal
{
public override string Sound { get; } = "Meow";
public bool Declawed { get; set; }
}
[TestMethod]
public void Demo()
{
var input = #"{""Sound"":""Bark"",""Breed"":""Jack Russell Terrier""}"
var animal = JsonConvert.DeserializeObject<Animal>(input);
Assert.AreEqual("Jack Russell Terrier", (animal as Dog)?.Breed);
}
the converter implementation can be directly downloaded from the repository: JsonSubtypes.cs and is also availble as a nuget package
Use this JsonKnownTypes, it's very similar way to use, add couple of attribute:
[JsonConverter(typeof(JsonKnownTypeConverter<Organization>))]
[JsonDiscriminator(Name = "discriminator")]
[JsonKnownType(typeof(Company), "company")]
[JsonKnownType(typeof(NonProfitOrganization), "non-profit")]
public abstract class Organization
{
/* properties related to all organizations */
}
public sealed class Company : Organization
{
/* properties related to companies */
}
public sealed class NonProfitOrganization : Organization
{
/* properties related to non profit organizations */
}
And serialize:
var json = JsonConvert.SerializeObject(youObject)
Output json:
{..., "discriminator":"non-profit"} //if object was NonProfitOrganization
Deserialization:
var organization = JsonConvert.DeserializeObject<Organization>(payload);

.NET NewtonSoft JSON deserialize with different property name [duplicate]

how can I deserialize below json structure using newtonsoft json.net in .net.
{
"users" : {
"parentname":"test",
"100034" : {
"name" : "tom",
"state" : "WA",
"id" : "cedf-c56f-18a4-4b1"
},
"10045" : {
"name" : "steve",
"state" : "NY",
"id" : "ebb2-92bf-3062-7774"
},
"12345" : {
"name" : "mike",
"state" : "MA",
"id" : "fb60-b34f-6dc8-aaf7"
}
}
}
I tried below code but its not working. I got error 'Error converting value "test" to type 'ConsoleApplication2.User'. Path 'users.parentname', line 5, position 35.'
class Program
{
static void Main(string[] args)
{
string json = #"
{
""users"": {
""parentname"":""test"",
""10045"": {
""name"": ""steve"",
""state"": ""NY"",
""id"": ""ebb2-92bf-3062-7774""
}
}
}";
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
}
}
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Please suggest.
You have a couple problems:
Your JSON has an extra level of nesting, with the root object containing a single property "users":
{
"users" : { ... }
}
Your data model needs to reflect this.
Your "users" object has a mixture of known and unknown property names. The question Deserialize json with known and unknown fields addresses a similar situation, however in your case your unknown properties always have a fixed schema and their values should be deserialized into a dictionary of POCOs -- specifically the User class. Therefore the answers there don't quite meet your needs, nor does the build-in functionality [JsonExtensionData].
The following converter allows for unknown properties to be deserialized into a typed container, rather than into an dictionary of arbitrary types:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class JsonTypedExtensionDataAttribute : Attribute
{
}
public class TypedExtensionDataConverter<TObject> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(TObject).IsAssignableFrom(objectType);
}
JsonProperty GetExtensionJsonProperty(JsonObjectContract contract)
{
try
{
return contract.Properties.Where(p => p.AttributeProvider.GetAttributes(typeof(JsonTypedExtensionDataAttribute), false).Any()).Single();
}
catch (InvalidOperationException ex)
{
throw new JsonSerializationException(string.Format("Exactly one property with JsonTypedExtensionDataAttribute is required for type {0}", contract.UnderlyingType), ex);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jObj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var extensionJsonProperty = GetExtensionJsonProperty(contract);
var extensionJProperty = (JProperty)null;
for (int i = jObj.Count - 1; i >= 0; i--)
{
var property = (JProperty)jObj.AsList()[i];
if (contract.Properties.GetClosestMatchProperty(property.Name) == null)
{
if (extensionJProperty == null)
{
extensionJProperty = new JProperty(extensionJsonProperty.PropertyName, new JObject());
jObj.Add(extensionJProperty);
}
((JObject)extensionJProperty.Value).Add(property.RemoveFromLowestPossibleParent());
}
}
var value = existingValue ?? contract.DefaultCreator();
using (var subReader = jObj.CreateReader())
serializer.Populate(subReader, value);
return value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
var extensionJsonProperty = GetExtensionJsonProperty(contract);
JObject jObj;
using (new PushValue<bool>(true, () => Disabled, (canWrite) => Disabled = canWrite))
{
jObj = JObject.FromObject(value, serializer);
}
var extensionValue = (jObj[extensionJsonProperty.PropertyName] as JObject).RemoveFromLowestPossibleParent();
if (extensionValue != null)
{
for (int i = extensionValue.Count - 1; i >= 0; i--)
{
var property = (JProperty)extensionValue.AsList()[i];
jObj.Add(property.RemoveFromLowestPossibleParent());
}
}
jObj.WriteTo(writer);
}
[ThreadStatic]
static bool disabled;
// Disables the converter in a thread-safe manner.
bool Disabled { get { return disabled; } set { disabled = value; } }
public override bool CanWrite { get { return !Disabled; } }
public override bool CanRead { get { return !Disabled; } }
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
public static class JsonExtensions
{
public static TJToken RemoveFromLowestPossibleParent<TJToken>(this TJToken node) where TJToken : JToken
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
public static IList<JToken> AsList(this IList<JToken> container) { return container; }
}
Then use it in your classes as follows:
class RootObject
{
[JsonProperty("users")]
public Users Users { get; set; }
}
[JsonConverter(typeof(TypedExtensionDataConverter<Users>))]
class Users
{
public Users()
{
this.UserTable = new Dictionary<string, User>();
}
[JsonProperty("parentname")]
public string ParentName { get; set; }
[JsonTypedExtensionData]
public Dictionary<string, User> UserTable { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
}
I wrote the converter in a fairly general way so it can be reused. A converter that is hardcoded for the Users type would require less code.
Your Json has to look like this:
{
"ParentName":"test",
"users":{
"10045":{
"name":"steve",
"state":"NY",
"id":"ebb2-92bf-3062-7774",
"ParentName":"someOtherName"
}
}
}
In order to deserialize it with your given class structure:
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Now you can deserialize the Json string with:
var root = JsonConvert.DeserializeObject<RootObject>(json);

Custom Json Converter With Constructor Arguments

I am trying to create a custom Json converter that has no default constructor and instead takes a factory that is dependency injected by Autofac. When ever I hit the object that uses this converter I get an exception that there is no no-arg constructor to use for the deserialization.
I have an objects and primitives. One of the objects is an abstract base object that I have the converter on. Since this converter is abstract I want to dependency inject a factory into the converter's ReadJson method to make the choice as to what conversion to make.
Currently the code is something like the following:
using System;
using System.Collections.Generic;
using Autofac;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Newtonsoft = Newtonsoft.Json;
public class JsonModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<SubThingFactory>()
.As<IFactory>()
.SingleInstance();
builder.Register(c => this.CreateJsonSerializerSettings(c)).SingleInstance();
builder.RegisterType<CamelCasePropertyNamesContractResolver>()
.As<IContractResolver>()
.SingleInstance();
builder.RegisterType<IsoDateTimeConverter>()
.As<Newtonsoft.JsonConverter>()
.SingleInstance();
builder.RegisterType<SubThingConverter>()
.As<Newtonsoft.JsonConverter>()
.SingleInstance();
builder.Register(c => new StringEnumConverter
{
CamelCaseText = true
})
.As<Newtonsoft.JsonConverter>()
.SingleInstance();
}
private Newtonsoft.JsonSerializerSettings CreateJsonSerializerSettings(IComponentContext context)
{
var settings = new Newtonsoft.JsonSerializerSettings
{
DefaultValueHandling = Newtonsoft.DefaultValueHandling.Ignore,
NullValueHandling = Newtonsoft.NullValueHandling.Ignore,
DateTimeZoneHandling = Newtonsoft.DateTimeZoneHandling.Utc
};
settings.ContractResolver = context.Resolve<IContractResolver>();
foreach (var converter in context.Resolve<IEnumerable<Newtonsoft.JsonConverter>>())
{
settings.Converters.Add(converter);
}
return settings;
}
}
public class ThingBeingDeserialized
{
private string Name;
private SubThing subby;
}
[Newtonsoft.JsonConverterAttribute(typeof(SubThingConverter))]
public abstract class SubThing
{
public string Name { get; set; }
public virtual string GetName()
{
//Uses reflection to get the name from a custom attribute
return this.Name;
}
}
[CustomName("A")]
public class SubThingA : SubThing
{
public int Field1 { get; set; }
}
[CustomName("B")]
public class SubThingB : SubThing
{
public string Field2 { get; set; }
}
public class SubThingConverter : Newtonsoft.JsonConverter
{
//This is Autofac injected in
private readonly IFactory factory;
public SubThingConverter(IFactory factory)
{
this.factory = factory;
}
public override object ReadJson(Newtonsoft.JsonReader reader, Type objectType, object existingValue, Newtonsoft.JsonSerializer serializer)
{
if (reader.TokenType == Newtonsoft.JsonToken.Null)
{
return null;
}
var jsonObject = JObject.Load(reader);
var type = jsonObject["type"].ToString();
return this.factory.GetSubThing(type, jsonObject);
}
public override void WriteJson(Newtonsoft.JsonWriter writer, object value, Newtonsoft.JsonSerializer serializer)
{
var type = value.GetType();
var properties = type.GetProperties();
var jObject = new JObject
{
{ "type", type.Name }
};
foreach (var prop in properties)
{
if (prop.CanRead)
{
var propVal = prop.GetValue(value, null);
if (propVal != null)
{
jObject.Add(prop.Name, JToken.FromObject(propVal, serializer));
}
}
}
jObject.WriteTo(writer);
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SubThing);
}
}
public interface IFactory
{
SubThing GetSubThing(string type, JObject restOfObj);
}
public class SubThingFactory : IFactory
{
public SubThing GetSubThing(string type, JObject restOfObj)
{
switch (type)
{
case "A":
return new SubThingA
{
Field1 = (int)(restOfObj["Field1"])
};
case "B":
return new SubThingB
{
Field2 = (string)(restOfObj["Field2"])
};
}
return null;
}
}
public class CustomNameAttribute : Attribute
{
public CustomNameAttribute(string name)
{
this.Name = name;
}
public string Name { get; set; }
}
The way I am doing the Autofac injection for the JsonSerializerSettings is by registering the settings such that the settings.Converters will pick up the enumeration of all the JsonConverters that are registered with the Autofac container and the SubThingConverter is registered such that when it is resolved it will have the IFactory resolved for it and the JsonSerializer also comes from the autofac container with these settings.
Even when I skip the dependency injection and use a new JsonSerializer with the JsonSerializerSettings with the custom converter added as
settings.Converters.Add(new SubThingConverter(new SubThingFactory()))
I still get the complaint that the SubThingConverter does not have a no arg constructor.
It seems to me that overridding the settings to explicitly use this converter should be enough. I also tried adding in the object[] params in the JsonConverter attribute on the SubThing, I couldn't get it to work and it seems to need to be a compile time array, which doesn't work with the dependency injection I need to do. Any pointers would be greatly appreciated. Thank you!

Service Stack POST-Request Body-Format / Transformation

iam using a RequestClass with the Route anotation to call a Json-Client POST method.
Now, while the paramters are structured like this
public class GetTicketRequest: IReturn<JsonObject>
{
public string CartId {
get;
set;
}
public string PriceId {
get;
set;
}
}
The BackendAPI needs them to be nesten in "data" in the json request, so more like
{
"data":[
{"cartid":123,
"priceId":11}]
}
Is there any way to transfrom the request object for the body before calling
JsonServiceClient _restClient = new JsonServiceClient(baseUrl);
JsonObject oneResponse = _restClient.Post(options);
This solution is useful where many DTOs require to be wrapped & converted, and is highly reusable, with no changes to your existing DTOs.
You can convert the requests of the JsonServiceClient by overriding the methods that handle preparing the requests for sending. Which means implementing your own extended JsonServiceClient as given below.
If you want to do this for all verbs then you override it's Send<TResponse> methods (otherwise, if it's just for POST then uncomment the commented out code, and remove the Send methods).
public class MyJsonServiceClient : JsonServiceClient
{
public Dictionary<Type, Func<object, object>> DtoConverters = new Dictionary<Type, Func<object, object>>();
public MyJsonServiceClient() {}
public MyJsonServiceClient(string baseUri) : base(baseUri) {}
public MyJsonServiceClient(string syncReplyBaseUri, string asyncOneWayBaseUri) : base(syncReplyBaseUri, asyncOneWayBaseUri) {}
public override TResponse Send<TResponse>(object request)
{
return base.Send<TResponse>(ConvertRequest(request));
}
public override TResponse Send<TResponse>(string httpMethod, string relativeOrAbsoluteUrl, object request)
{
return base.Send<TResponse>(httpMethod, relativeOrAbsoluteUrl, ConvertRequest(request));
}
/*
public override TResponse Post<TResponse>(string relativeOrAbsoluteUrl, object requestDto)
{
return base.Post(relativeOrAbsoluteUrl, ConvertRequest(requestDto));
}
*/
object ConvertRequest(object request)
{
Type dtoType = request.GetType();
return (DtoConverters.ContainsKey(dtoType)) ? DtoConverters[dtoType](request) : request;
}
}
Usage:
So given this DTO:
[Route("/test", "POST")]
public class TicketRequest : IReturnVoid
{
public string CartId { get; set; }
public string PriceId { get; set; }
}
You simply add the converter:
var client = new MyJsonServiceClient("http://localhost:9000");
// Simple converter for TicketRequest
client.DtoConverters.Add(typeof(TicketRequest), dto => {
var d = (TicketRequest)dto;
return new {
data = new {
CartId = d.CartId.ToInt(),
PriceId = d.PriceId.ToInt()
}
};
});
client.Post(new TicketRequest { CartId = "123", PriceId = "456" });
i solved this issue using a typed data property
public class GetTicketRequest: IReturn<JsonObject>
{
public class TicketCreateData
{
public int priceId {
get;
set;
}
}
public string CartId {
get;
set;
}
public string PriceId {
get;
set;
}
public List<TicketCreateData> data {
get {
var list = new List<TicketCreateData>();
list.Add(new TicketCreateData {
priceId = this.PriceId.ToInt()
});
return list;
}
set {
data = value;
}
}
}
To notes on this:
if neede, use DataContract/DataMember(Name="") to rename fields or only do partial serializing
Do never use structs for, like in this case, the data class - they are not serializeable at all
in my spefici case data even needs to be an array, thats why i used the list

WCF Data Services 5.0 Workaround for returning POCOs?

I'm pretty sure it hasn't, but apologies if this question has already been asked. And additional apologies if this is just flat out a dumb question but I feel like I'm either completely missing something or have the right idea and just need some backup for my own sanity.
I've been implementing WCF Data Services 5.0 in our application and am having no issues with read operations returning entity objects.
Unfortunately there is that nasty limitation when it comes to service operations that they can only return primitive types (See MSDN). It's very annoying given that it has no problems with the entity objects.
I know that one workaround is to create a "dummy" complex type since WCFDS will recognize that but I don't want to just throw random POCOs into my data model that aren't actually in the database.
So the solution that occurred to me was to create an extension method for my objects that can serialize them into JSON strings to be returned by the service. My question is; are there any compelling arguments why I shouldn't do this or can anyone suggest any better alternatives?
Edit: Additional information to clarify my current issues
I created a very simple example of what I'm doing that originally raised this question. My service class follows first:
[JsonpSupportBehavior]
public partial class SchedulingService : DataService<ChronosDataContext>, ISchedulingService
{
public static void InitializeService(DataServiceConfiguration config)
{
#if DEBUG
config.UseVerboseErrors = true;
#endif
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
config.SetServiceOperationAccessRule(
"TestService",
ServiceOperationRights.All);
}
[WebGet]
public SchedulingResult TestService(
string testParam1,
string testParam2)
{
// NOTE: I never use the params, they're just there for this example.
SchedulingResult result = SchedulingResult.Empty;
result.Status = OperationStatus.Success;
result.ResponseID = Guid.NewGuid();
result.AffectedIDs = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7 });
result.RecordsAffected = 10;
return result;
}
}
Attempting to access this service using my browser, I get the following request error:
The server encountered an error processing the request. The exception message is
'Unable to load metadata for return type
'Chronos.Services.SchedulingResult' of method
'Chronos.Services.SchedulingResult TestService(System.String, System.String)'.'.
See server logs for more details.
The exception stack trace is:
at System.Data.Services.Providers.BaseServiceProvider.AddServiceOperation(MethodInfo method, String protocolMethod)
at System.Data.Services.Providers.BaseServiceProvider.AddOperationsFromType(Type type)
at System.Data.Services.Providers.BaseServiceProvider.LoadMetadata()
at System.Data.Services.DataService`1.CreateMetadataAndQueryProviders(IDataServiceMetadataProvider& metadataProviderInstance, IDataServiceQueryProvider& queryProviderInstance, BaseServiceProvider& builtInProvider, Object& dataSourceInstance)
at System.Data.Services.DataService`1.CreateProvider()
at System.Data.Services.DataService`1.HandleRequest()
at System.Data.Services.DataService`1.ProcessRequestForMessage(Stream messageBody)
at SyncInvokeProcessRequestForMessage(Object , Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
Below are the classes that make up the SchedulingResult that I'm trying to return:
public class SchedulingResult : ServiceInvocationResponse
{
public SchedulingResult()
: base()
{
this.Payload = new object[]
{
new List<int>(),
new List<int>()
};
}
public List<int> AffectedIDs
{
get { return (List<int>)Payload[0]; }
set { Payload[0] = value; }
}
public List<int> FailedIDs
{
get { return (List<int>)Payload[1]; }
set { Payload[1] = value; }
}
public static SchedulingResult Empty
{
get { return new SchedulingResult(); }
}
}
public class ServiceInvocationResponse : AbstractJsonObject<ServiceInvocationResponse>
{
public ServiceInvocationResponse()
{
this.Status = OperationStatus.Unknown;
this.Severity = ErrorSeverity.None;
}
public virtual int RecordsAffected { get; set; }
public virtual Exception ErrorObject { get; set; }
internal virtual object[] Payload { get; set; }
}
public abstract class AbstractJsonObject<TBaseType>
{
public virtual object Deserialize(string source)
{
return JsonConvert.DeserializeObject(source);
}
public virtual T Deserialize<T>(string source)
{
return JsonConvert.DeserializeObject<T>(source);
}
public string Serialize()
{
return JsonConvert.SerializeObject(
this, Formatting.Indented);
}
public override string ToString()
{
return this.Serialize();
}
public static TBaseType FromString(string json)
{
return JsonConvert.DeserializeObject<TBaseType>(json);
}
}
It is possible to return one or many primitive, complex, or entity types from a service operation.
A primitive type is what you'd expect: string, int, bool, etc.
A complex type is a class that doesn't have a unique key (a property named ID or the [DataServiceKey("<yourkeyhere>")] attribute)
An entity type is a class that does have a unique key
For instance:
using System.Data.Services;
using System.Data.Services.Common;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Web;
namespace Scratch.Web
{
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class ScratchService : DataService<ScratchContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
config.UseVerboseErrors = true;
}
[WebGet]
public string GetPrimitive()
{
return "Success";
}
[WebGet]
public IQueryable<string> GetPrimitives()
{
return new[] { "Success", "Hello World" }.AsQueryable();
}
[WebGet]
public ComplexType GetComplexType()
{
return new ComplexType { Property1 = "Success", Property2 = "Hello World" };
}
[WebGet]
public IQueryable<ComplexType> GetComplexTypes()
{
return new[] {
new ComplexType { Property1 = "Success", Property2 = "Hello World" },
new ComplexType { Property1 = "Success", Property2 = "Hello World" }
}.AsQueryable();
}
[WebGet]
public EntityType GetEntityType()
{
return new EntityType { Property1 = "Success", Property2 = "Hello World" };
}
[WebGet]
public IQueryable<EntityType> GetEntityTypes()
{
return new[] {
new EntityType { Property1 = "Success1", Property2 = "Hello World" },
new EntityType { Property1 = "Success2", Property2 = "Hello World" }
}.AsQueryable();
}
}
public class ScratchContext { }
public class ComplexType
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
[DataServiceKey("Property1")]
public class EntityType
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
}
Perhaps you're running into some other problem?