反序列化使用TypeNameHandling.All序列化的字符串 [英] deserialize string that was serialized with TypeNameHandling.All

查看:107
本文介绍了反序列化使用TypeNameHandling.All序列化的字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用以下示例对类进行了序列化

A class was serialized using the following sample

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

The above sample produces the following 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"
      }
    ]
  }
}

现在,在另一个无法访问上述模型的程序上,
我必须将字符串反序列化为一个对象,但是我尝试过的任何方法似乎都没有效果……

Now,on another program ,that doesn't have access to the above models,
I have to deserialize the string to an object but nothing I've tried seems to be working...

要创建新模型,我将json复制到剪贴板上,并使用了Visual Studio的选择性粘贴"功能

To create my new models I copied the json on my clipboard and used Visual Studio's "Paste Special" functionality

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

}

我可以对此做任何事情,还是应该尝试查找原始类?

Can I do anything about this or I should try to find the original classes?

更新

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

I don't care about the "$type" property. It's not even on the original models. I just want to deserialize the JSON to a strongly typed model, including the collections (my real classes have more nested levels) but the auto generated types (using Paste Json) don't work.

推荐答案

如果您要做的只是忽略类型信息,则:

If all you want to do is to ignore the type information, then:

  • 如果使用TypeNameHandling.None反序列化,则对象上的"$type"属性将被简单地忽略,在反序列化期间不会造成任何问题.

  • If you deserialize with TypeNameHandling.None, then "$type" properties on objects are simply ignored will cause no problem during deserialization.

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

But even with TypeNameHandling.None, "$type" properties for collection values cause problems because the type metadata generated for collection contains forces an extra level of nesting in the JSON:

使用"type":

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

并且没有:

{
  "Actors": []
}

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

When deserializing JSON with TypeNameHandling.None, if a serialized collection with the extra nesting level is encountered, an exception gets thrown.

因此,您需要某种方法来消除反序列化过程中多余的嵌套层,例如使用自定义JsonConverter .并且在此问题的答案在版本/格式之间迁移序列化Json.NET文档的策略,已经编写并且可以使用:IgnoreCollectionTypeConverter.

So you need some way to strip off the extra level of nesting during deserialization, e.g. with a custom JsonConverter. And in this answer to the question Strategies for migrating serialized Json.NET document between versions/formats there is one already written and available for use: IgnoreCollectionTypeConverter.

因此,您可以按以下方式定义模型:

Thus you can define your models as follows:

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

示例小提琴.

注意:

  • IgnoreCollectionTypeConverter设计用于读取/写入集合,这就是为什么我将Actors从数组更改为List<T>的原因.

  • IgnoreCollectionTypeConverter is designed to work with read/write collections, which is why I changed Actors from an array to a List<T>.

如果您需要处理而不是忽略类型信息,则需要创建自定义自定义SerializationBinder .问题如何为二进制格式化程序创建SerializationBinder,以处理类型从一个程序集和名称空间到另一个程序集的迁移,为创建一个用于处理泛型嵌套的自定义序列化活页夹.

If you need to process the type information rather than ignore it, you will need to create a custom ISerializationBinder. See Custom SerializationBinder for details. The question How to create a SerializationBinder for the Binary Formatter that handles the moving of types from one assembly and namespace to another gives a solution for creating a custom serialization binder that handles nesting of generics.

更新

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

You asked, I just want to deserialize the json to a strongly typed model ,including the collections (my real classes have more nested levels) but the auto generated types (using Paste Json) don't work.

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

During your development process, you can use LINQ to JSON to load your JSON into memory, remove all "$type" metadata, and write out to a new JSON string. Then, you can take that cleaned string and use it for "Paste Json as Classes".

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

The following extension method will do the necessary work:

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小提琴,它将获取您输入的JSON字符串并返回:

Sample working .Net fiddle which takes your input JSON string and returns:

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

这篇关于反序列化使用TypeNameHandling.All序列化的字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆