2

我有 JSON 数据,我想将其转换为正确的类型,然后对其进行处理。我正在使用 MONO 和 NewtonSoft 的 JSON 库。IE 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!");
    }

}

}

4

2 回答 2

1

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

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

  1. 解析 JSON 并创建具有相应属性的对象
  2. 确定哪个 DTO 实例与给定数据匹配
  3. 创建 DTO 实例并使用在步骤 1 中创建的对象填充它

我假设您拥有或可以找到一个 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 助手——它会自动找到一个构造函数来调用和复制任何剩余的匹配属性。

我希望这能为您指明正确的方向。

于 2013-02-02T20:57:08.207 回答
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);
        }

    }

}
于 2013-02-05T20:25:38.083 回答