反序列化使用TypeNameHandling.All序列化的字符串 [英] deserialize string that was serialized with 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 changedActors
from an array to aList<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屋!