反序列化使用TypeNameHandling.All序列化的字符串

时间:2017-12-13 21:28:03

标签: c# json.net

使用以下示例

序列化了一个类
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" Paste Special"功能

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; }
    }

}

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

更新

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

1 个答案:

答案 0 :(得分:2)

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

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

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

    使用"type"

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

    没有:

    {
      "Actors": []
    }
    

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

    因此,您需要某种方法在反序列化期间去除额外的嵌套级别,例如用custom JsonConverter。在问题this answerStrategies 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)不起作用。< / em>的

在开发过程中,您可以使用LINQ to JSON将JSON加载到内存中,删除所有"$type"元数据,然后写出新的JSON字符串。然后,您可以获取该已清理的字符串并将其用于“将Json粘贴为类”。

以下扩展方法将执行必要的工作:

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"
    }
  ]
}