【问题标题】:How to Dynamically convert JSON to right DTO class?如何将 JSON 动态转换为正确的 DTO 类?
【发布时间】:2013-02-02 15:57:15
【问题描述】:

我有 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!");
    }

}

}

【问题讨论】:

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


    【解决方案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 助手 - 它会自动找到一个构造函数来调用和复制任何剩余的匹配属性。

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

    【讨论】:

      【解决方案2】:

      我让它工作了。我必须添加带有 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);
              }
      
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-10
        • 2016-06-08
        • 1970-01-01
        • 2014-05-16
        • 1970-01-01
        • 2021-11-21
        • 1970-01-01
        • 2017-04-01
        相关资源
        最近更新 更多