Convert JObject to anonymous object

10,923

Solution 1

Json objects can not be fully serialized as an object. If you are using Newtonsoft it converts unknown type to a JObject. In this problem we tried to create unknown type on runtime. In order to do this on the wep api side we have to pass the type detail into the web api application. Json.NET Schema library can serialize a Type to a string. This method helps us to pass the unknown types' schema to the web api application. From the web api side two parameters needed. First parameter is json schema string and the second one is json data string. At this point by the help of json schema string we can generate this type on runtime by using Reflection library. Here is the C# class. But it doesnt works for lists or arrays. In the future I can develop it.

  public class ObjectConverter
{
    public static object Convert(string json, JSchema schema)
    {
        var type = CreateType(schema);
        var destObject = Newtonsoft.Json.JsonConvert.DeserializeObject(json, type);
        return destObject;
    }

    private static Type CreateType(JSchema schema)
    {
        Type result = null;
        var typeBuilder = GetTypeBuilder(Guid.NewGuid().ToString());
        foreach (var item in schema.Properties)
        {
            if (item.Value.Type == (Newtonsoft.Json.Schema.JSchemaType.Object | Newtonsoft.Json.Schema.JSchemaType.Null))
            {
                Type type = CreateType(item.Value);
                if (item.Value.Type != null)
                {
                    CreateProperty(typeBuilder, item.Key, type);
                }
            }
            else
            {
                if (item.Value.Type != null)
                {
                    CreateProperty(typeBuilder, item.Key, ConvertType(item.Value.Type.Value));
                }
            }
        }

        result = typeBuilder.CreateType();
        return result;
    }

    private static Type ConvertType(JSchemaType source)
    {
        Type result = null;
        switch (source)
        {
            case JSchemaType.None:

                break;
            case JSchemaType.String:
                result = typeof(string);
                break;
            case JSchemaType.Number:
                result = typeof(float);
                break;
            case JSchemaType.Integer:
                result = typeof(int);
                break;
            case JSchemaType.Boolean:
                result = typeof(bool);
                break;
            case JSchemaType.Object:
                result = typeof(object);
                break;
            case JSchemaType.Array:
                result = typeof(Array);
                break;
            case JSchemaType.Null:
                result = typeof(Nullable);
                break;
            case Newtonsoft.Json.Schema.JSchemaType.String | Newtonsoft.Json.Schema.JSchemaType.Null:
                result = typeof(string);
                break;
            default:
                break;
        }
        return result;
    }

    private static TypeBuilder GetTypeBuilder(string typeSignature)
    {
        var an = new AssemblyName(typeSignature);
        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
        TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
                TypeAttributes.Public |
                TypeAttributes.Class |
                TypeAttributes.AutoClass |
                TypeAttributes.AnsiClass |
                TypeAttributes.BeforeFieldInit |
                TypeAttributes.AutoLayout,
                null);
        return tb;
    }

    private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
    {
        FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

        PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
        MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
        ILGenerator getIl = getPropMthdBldr.GetILGenerator();

        getIl.Emit(OpCodes.Ldarg_0);
        getIl.Emit(OpCodes.Ldfld, fieldBuilder);
        getIl.Emit(OpCodes.Ret);

        MethodBuilder setPropMthdBldr =
            tb.DefineMethod("set_" + propertyName,
              MethodAttributes.Public |
              MethodAttributes.SpecialName |
              MethodAttributes.HideBySig,
              null, new[] { propertyType });

        ILGenerator setIl = setPropMthdBldr.GetILGenerator();
        Label modifyProperty = setIl.DefineLabel();
        Label exitSet = setIl.DefineLabel();

        setIl.MarkLabel(modifyProperty);
        setIl.Emit(OpCodes.Ldarg_0);
        setIl.Emit(OpCodes.Ldarg_1);
        setIl.Emit(OpCodes.Stfld, fieldBuilder);

        setIl.Emit(OpCodes.Nop);
        setIl.MarkLabel(exitSet);
        setIl.Emit(OpCodes.Ret);

        propertyBuilder.SetGetMethod(getPropMthdBldr);
        propertyBuilder.SetSetMethod(setPropMthdBldr);
    }
}

The Convert method helps to generate object from json data by the help of json schema. Just using Newtonsoft.Json.JsonConvert.DeserializeObject(json, type) command we can get a real object. It works with Telerik Reporting as a ObjectDataSource.

Solution 2

The closest I can find using Newtonsoft's deserializer is

dynamic d = JObject.Parse("{a:1000, b:'c', d: [1,2,3]}");

See Deserialize json object into dynamic object using Json.net

Cheers

Solution 3

Probably too old a conversation now to be useful since JsonConvert.DeserializeAnonymousType exists now but having looked at this post specifically for JObject I came up with a slightly simpler option which someone might in the future want to use :)

I created the extension method below which simply wraps the standard JObject.ToObject method.

 public static T ToAnonymousType<T>(this JObject source, T destinationType)
 {
     return source.ToObject<T>();
 }

This can then be used like this:

JObject myJObject = new JObject(); // Something more exciting probably happens here
var myAnonymousObject = myJObject.ToAnonymousType(new { Id = default(int), Name = default(string) });
Share:
10,923
Baran
Author by

Baran

Industrial Engineer

Updated on July 03, 2022

Comments

  • Baran
    Baran almost 2 years

    I am trying to pass an object into a web api application. While serializing the object it converts it to a json string. But on the wep api application side, it gets the object parameter as a JObject. This code block is from the web api application;

    //Read web api body content into a string variable
    var resultStr = Request.Content.ReadAsStringAsync().Result;
    //Convert json string to object with Newtonsoft
    var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<object>(resultStr);
    

    This code generates a JObject but I need an anonymous object. Web api project doesn't knows the types. Any object types can be received.

    I need an object like this.

    object anonyObj = new { Prop1 = "Lorem" };
    

    ExpandoObject type doesn't meet my requirements so converting to a dynamic type is not a solution for me.