如何将JSON动态转换为正确的DTO类?

时间:2013-02-02 15:57:16

标签: c# casting mono type-conversion dynamic-data

我有JSON数据,我想将其转换为正确的类型,然后处理它。我正在使用MONO和NewtonSoft的JSON库。 I.E. JSON和对象必须匹配属性1:1才能转换为右DTO。 DTO始终具有独特的属性。

Activator.CreateInstance()和Convert.ChangeType()似乎都没有编译。

的DTO:

class JSONDTO
{

}

class JSONCommandDTO : JSONDTO
{
    public string cmd;
}

class JSONProfileDTO : JSONDTO
{
    public string nick;
    public string name;
    public string email;
}

class JSONMessageDTO : JSONDTO
{
    public string msg;
}

服务器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Newtonsoft.Json;

class Server
{
    protected static List<JSONDTO> DTOList; 

    static void Main()
    {
        DTOList = new List<JSONDTO>();

        DTOList.Add(new JSONProfileDTO());
        DTOList.Add(new JSONCommandDTO());
        DTOList.Add(new JSONMessageDTO());

        // ...

    }

    protected static void OnMessage (string message)
    {
        dynamic rawObject;

        try
        {
            // Or try to convert to right DTO here somehow?
            rawObject = JsonConvert.DeserializeObject<dynamic>(message);
        } catch (JsonReaderException ex) {
            // Invalid JSON
            return;
        }

        int dtoCount = DTOList.ToArray().Length;
        int errCount = 0;

        JSONDTO DTOObject;

        foreach (var dto in DTOList.ToList()) {
            try {
                // Doesn't compile:

                // DTOObject = Activator.CreateInstance(dto.GetType(), rawObject);
                // DTOObject = Convert.ChangeType(rawObject, dto.GetType());
                break; // Match found!
            } catch (Exception ex) {
                // Didn't match
                errCount++;
            }
        }

        if (errCount == dtoCount) {
            // Right DTO was not found
            return;
        }


        if (DTOObject is JSONProfileDTO) {
            AssignProfile((JSONProfileDTO) DTOObject);
        }
        else if (DTOObject is JSONCommandDTO)
        {
            RunCommand((JSONCommandDTO) DTOObject);
        }
        // etc ..

    }

    protected static void RunCommand (JSONCommandDTO command)
    {
        string cmd = command.cmd;

        Console.WriteLine("command == " + cmd);
    }

    protected static void AssignProfile(JSONProfileDTO profile)
    {
        Console.WriteLine("got profile!");
    }

}

}

2 个答案:

答案 0 :(得分:1)

我将假设您没有自己从DTO类创建序列化数据,因为在这种情况下,您可以简单地在输出中包含类型信息。有了这些信息,解串器将能够自动重新创建正确的实例。

由于这很可能不是您的情况,您需要解决以下问题:

  1. 解析JSON并创建具有相应属性的对象
  2. 确定哪个DTO实例与给定数据匹配
  3. 创建DTO实例并使用在步骤1中创建的对象填充它
  4. 我假设您已经或者可以找到JSON反序列化器来处理第一步。

    您可能有更简单的方法来执行第2步,但简单的方法只是比较JSON数据中可用的属性名称,并找到完全匹配的DTO。这可能看起来像这样(使用Fasterflect来协助反射位):

    var types = [ typeof(JSONCommandDTO), typeof(JSONProfileDTO), typeof(JSONMessageDTO) ];
    var json = deserializer.GetInstance( ... );
    var jsonPropertyNames = json.GetType().Properties( Flags.InstancePublic )
        .OrderBy( p => p.Name );
    var match = types.FirstOrDefault( t => t.Properties( Flags.InstancePublic )
        .OrderBy( p => p.Name )
        .SequenceEqual( jsonPropertyNames ) );
    if( match != null ) // got match, proceed to step 3 
    

    步骤3的代码可能如下所示:

    // match is the DTO type to create
    var dto = match.TryCreateInstance( json );
    

    TryCreateInstance是另一个Fasterflect助手 - 它会自动找到一个构造函数来调用和复制任何剩余的匹配属性。

    我希望这能指出你正确的方向。

答案 1 :(得分:1)

我得到了它的工作。我不得不使用MissingMemberHandling.Error添加JsonSerializerSettings,以便在JSON不适合对象时抛出异常。我也缺少Microsoft.CSharp参考。

class Server
{
    protected static List<Type> DTOList = new List<Type>(); 

    static void Main()
    {
        DTOList.Add(typeof(JSONProfileDTO));
        DTOList.Add(typeof(JSONCommandDTO));
        DTOList.Add(typeof(JSONMessageDTO));
    }

    protected static void OnMessage (string rawString)
    {
        dynamic jsonObject = null;
        int DTOCount = DTOList.Count;
        int errors = 0;

        var settings = new JsonSerializerSettings ();

        // This was important
        // Now exception is thrown when creating invalid instance in the loop
        settings.MissingMemberHandling = MissingMemberHandling.Error;

        foreach (Type DTOType in DTOList) {
            try {
                jsonObject = JsonConvert.DeserializeObject (rawString, DTOType, settings);
                break;
            } catch (Exception ex) {
                errors++;
            }
        }

        if (null == jsonObject) {
            return;
        }

        if (errors == DTOCount) {
            return;
        }

        if (jsonObject is JSONProfileDTO) {
            AssignProfile((JSONProfileDTO) jsonObject);
        }
        else if (jsonObject is JSONCommandDTO)
        {
            RunCommand((JSONCommandDTO) jsonObject);
        }

    }

}