【问题标题】:deserialize string that was serialized with TypeNameHandling.All反序列化使用 TypeNameHandling.All 序列化的字符串
【发布时间】:2017-12-13 21:28:03
【问题描述】:

使用以下示例序列化了一个类

using Newtonsoft.Json;
using System;

namespace ConsoleAppCompare
{
    class Program
    {
        static void Main(string[] args)
        {
            Movie movie = new Movie()
            {
                Name = "Avengers",
                Language = "En",
                Actors = new Character[] { new Character(){Name="Phil Coulson"},new Character(){Name="Tony Stark"}
            }};
            Console.WriteLine(JsonConvert.SerializeObject(movie, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }));
            Console.ReadLine();
        }
    }

    class Movie
    {

        public string Name { get; set; }

        public string Language { get; set; }

        public Character[] Actors { get; set; }

    }

    class Character
    {
        public string Name { get; set; }
    }
}

以上示例生成以下 json

{
  "$type": "ConsoleAppCompare.Movie, ConsoleAppCompare",
  "Name": "Avengers",
  "Language": "En",
  "Actors": {
    "$type": "ConsoleAppCompare.Character[], ConsoleAppCompare",
    "$values": [
      {
        "$type": "ConsoleAppCompare.Character, ConsoleAppCompare",
        "Name": "Phil Coulson"
      },
      {
        "$type": "ConsoleAppCompare.Character, ConsoleAppCompare",
        "Name": "Tony Stark"
      }
    ]
  }
}

现在,在另一个程序 上,它无法访问上述模型,
我必须将字符串反序列化为一个对象,但我尝试过的任何方法似乎都不起作用......

为了创建我的新模型,我复制了剪贴板上的 json 并使用了 Visual Studio 的“选择性粘贴”功能

using Newtonsoft.Json;
using System;
using System.IO;


namespace ConsoleAppCompare
{
    class Program
    {
        static void Main(string[] args)
        {
            var s = File.ReadAllText(@"C:\Users\nvovo\Desktop\asdf\aa.txt");

            Rootobject movie = null;

             // nothing Works :(
            //movie =JsonConvert.DeserializeObject<Rootobject>(s, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
            //movie = JsonConvert.DeserializeObject<Rootobject>(s, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.None });
            //movie = JsonConvert.DeserializeObject<Rootobject>(s);
            Console.ReadLine();
        }
    }


    public class Rootobject
    {
        public string type { get; set; }
        public string Name { get; set; }
        public string Language { get; set; }
        public Actors Actors { get; set; }
    }

    public class Actors
    {
        public string type { get; set; }
        public Values[] values { get; set; }
    }

    public class Values
    {
        public string type { get; set; }
        public string Name { get; set; }
    }

}

我可以对此做些什么吗?或者我应该尝试找到原始课程?

更新

我不关心“$type”属性。它甚至不在原始模型上。我只想将 JSON 反序列化为强类型模型,包括集合(我的真实类有更多嵌套级别),但自动生成的类型(使用 Paste Json)不起作用。

【问题讨论】:

    标签: c# json.net


    【解决方案1】:

    如果您只想忽略类型信息,那么:

    • 如果您使用TypeNameHandling.None 进行反序列化,那么对象 上的"$type" 属性将在反序列化过程中被忽略。

    • 但即使使用TypeNameHandling.None集合值"$type"属性也会导致问题,因为为集合生成的类型元数据包含强制在 JSON 中进行额外级别的嵌套:

      "type":

      {
        "Actors": {
          "$type": "ConsoleAppCompare.Character[], ConsoleAppCompare",
          "$values": []
        }
      }
      

      没有:

      {
        "Actors": []
      }
      

      在使用TypeNameHandling.None 反序列化 JSON 时,如果遇到具有额外嵌套级别的序列化集合,则会引发异常。

      所以你需要一些方法来在反序列化过程中去除额外的嵌套级别,例如带有custom JsonConverter。在this answer 到问题Strategies for migrating serialized Json.NET document between versions/formats 中,有一个已经编写并可供使用:IgnoreCollectionTypeConverter

    因此,您可以如下定义模型:

    public class Rootobject
    {
        public string Name { get; set; }
        public string Language { get; set; }
        public List<Actor> Actors { get; set; }
    }
    
    public class Actor
    {
        public string Name { get; set; }
    }
    

    并反序列化如下:

    var settings = new JsonSerializerSettings
    {
        Converters = { new IgnoreCollectionTypeConverter() },
    };
    var movie = JsonConvert.DeserializeObject<Rootobject>(s, settings);
    

    示例fiddle

    注意事项:

    更新

    你问过,我只想将 json 反序列化为强类型模型,包括集合(我的真实类有更多嵌套级别),但自动生成的类型(使用 Paste Json)不起作用。

    在开发过程中,您可以使用 LINQ to JSON 将 JSON 加载到内存中,删除所有 "$type" 元数据,然后写入新的 JSON 字符串。然后,您可以将清理后的字符串用于“Paste Json as Classes”。

    以下扩展方法将完成必要的工作:

    public static class JsonExtensions
    {
        const string valuesName = "$values";
        const string typeName = "$type";
    
        public static JToken RemoveTypeMetadata(this JToken root)
        {
            if (root == null)
                throw new ArgumentNullException();
            var types = root.SelectTokens(".." + typeName).Select(v => (JProperty)v.Parent).ToList();
            foreach (var typeProperty in types)
            {
                var parent = (JObject)typeProperty.Parent;
                typeProperty.Remove();
                var valueProperty = parent.Property(valuesName);
                if (valueProperty != null && parent.Count == 1)
                {
                    // Bubble the $values collection up removing the synthetic container object.
                    var value = valueProperty.Value;
                    if (parent == root)
                    {
                        root = value;
                    }
                    // Remove the $values property, detach the value, then replace it in the parent's parent.
                    valueProperty.Remove();
                    valueProperty.Value = null;
                    if (parent.Parent != null)
                    {
                        parent.Replace(value);
                    }
                }
            }
            return root;
        }
    }
    

    工作示例.Net fiddle 接受您输入的 JSON 字符串并返回:

    {
      "Name": "Avengers",
      "Language": "En",
      "Actors": [
        {
          "Name": "Phil Coulson"
        },
        {
          "Name": "Tony Stark"
        }
      ]
    }
    

    【讨论】:

    • 感谢您的详细回答。不幸的是,我必须处理这些收藏品。如果可能,我会尝试重用现有模型或尝试您的建议。
    • @NickVovos - 不幸的是我必须处理集合。 - 你的意思是你必须从 $type 参数推断集合类型?这有点不寻常。常见的用例是从 JSON 中推断出对象类型,并让接收系统定义集合类型。但如果是这样,您是否可以编辑您的问题以提供 minimal reproducible example 显示您需要做什么?
    • @NickVovos - 或者您的问题是您尝试使用 Paste Json 作为类为您的 JSON 自动生成类型,而 $type 信息搞砸了?
    • 您最后的评论是正确的。我不关心“$type”属性。它甚至不在原始模型上。我只想将 json 反序列化为强类型模型,包括集合(我的真实类有更多嵌套级别)但自动生成的类型(使用 Paste Json)不起作用
    • 惊人的答案。非常感谢您的宝贵时间!它做了我想做的事!
    猜你喜欢
    • 2012-05-10
    • 2015-11-26
    • 2018-10-22
    • 2023-03-23
    • 2019-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多