如何将 JSON 反序列化为正确类型的对象,而无需事先定义类型?
Posted
技术标签:
【中文标题】如何将 JSON 反序列化为正确类型的对象,而无需事先定义类型?【英文标题】:How to deserialize JSON to objects of the correct type, without having to define the type before hand? 【发布时间】:2015-06-23 16:09:08 【问题描述】:我搜索了类似的问题,但找不到任何与我正在寻找的内容完全匹配的内容。
C# 新手,请多多包涵。
我有一些我正在反序列化的 json 文件。我希望文件反序列化为正确类型的对象,而不必事先定义类型。这是我的代码:
public class loadJson
//path of the file location
public void readJson(string path)
//array of files at the path location. right now just reading one file
FileInfo[] files = new DirectoryInfo(path).GetFiles("seleniumExample.json").ToArray();
foreach (FileInfo fi in files)
dynamic b1 = null;
using (StreamReader file = new StreamReader(fi.FullName))
string fileText = file.ReadToEnd();
//Console.WriteLine(fileText);
try
b1 = Newtonsoft.Json.JsonConvert.DeserializeObject(fileText);
catch(Exception e)
Console.WriteLine("ERROR!!!! " + e.ToString());
file.Close();
我有一堆对象类型,我将通过 json 文件将它们输入我的程序。
我不想将 b1 显式称为投标、客户或任何其他特定的预定义类。如果我明确地将 b1 称为投标,它会很好地加载所有信息并填写正确的实例变量。
但是当我使用“动态”或一般“对象”时,它无法弄清楚,只是初始化为“对象”。
有没有办法执行通用反序列化并让它根据 json 文件中定义的字段创建正确类的对象?
提前感谢您的帮助,如果我的问题非常不清楚,我深表歉意。如果是这样,请告诉我如何帮助消除任何歧义。再次感谢。
【问题讨论】:
Is there a way to perform generic deserialization and have it create an object of the correct class based on the fields defined in the json file?
假设 Json.Net 猜到了 正确 类并返回了该对象。您将如何在代码中使用它?在编写代码时,您仍然不知道 b1
的类型。
所以,我的想法是这样的:我认为我可以这样做以通用方式,不必重用太多代码。这没有意义吗?再说一次,我是 c# 和软件开发的新手,所以我对这可能不是最好的方法持开放态度。
你有能力控制 JSON 文件的格式和内容吗?或者它们是给定的?
我有能力控制格式和内容。
目录下的所有JSON文件是否都存储了相同类型的对象?
【参考方案1】:
Json.NET 能够在序列化过程中记录多态类型的 .Net 对象类型,通过使用设置TypeNameHandling = TypeNameHandling.Auto
。启用该设置后,.Net 类型的多态对象将显示为名为@987654336@、for instance 的合成属性:
"$type": "Newtonsoft.Json.Samples.Stockholder, Newtonsoft.Json.Tests"
但是,如果您调用常规方法 JsonConvert.SerializeObject(Object)
或 JsonSerializer.Serialize(TextWriter, Object)
,则永远不会为 root 对象发出 "$type"
属性。相反,您必须使用接受“预期”根类型的序列化方法之一,例如SerializeObject(Object, Type, JsonSerializerSettings)
或JsonSerializer.Serialize(TextWriter, Object, Type)
。传递 typeof(object)
作为预期类型保证 type 属性会出现:
var settings = new JsonSerializerSettings TypeNameHandling = TypeNameHandling.Auto ;
var json = JsonConvert.SerializeObject(rootObject, typeof(object), settings);
如果您使用此设置创建 JSON 文件,JSON 本身将记住序列化对象的类型。 Json.NET 在反序列化期间将使用此类型,只要您将TypeNameHandling
设置为TypeNameHandling.None
以外的其他值。例如:
var settings = new Newtonsoft.Json.JsonSerializerSettings TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto ;
b1 = Newtonsoft.Json.JsonConvert.DeserializeObject(fileText, settings);
工作示例 .Net fiddle here.
注意事项:这种在 JSON 中存储 .Net 类型的方式是非标准的。 DataContractJsonSerializer
等其他序列化程序不处理这种格式的类型信息。
还要注意Newtonsoft docs 的这一警告:
当您的应用程序从外部源反序列化 JSON 时,应谨慎使用 TypeNameHandling。使用 None 以外的值反序列化时,应使用自定义 SerializationBinder 验证传入类型。
关于为什么这可能是必要的讨论,请参阅 TypeNameHandling caution in Newtonsoft Json、How to configure Json.NET to create a vulnerable web API 和 Alvaro Muñoz 和 Oleksandr Mirosh 的黑帽论文 https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf。
【讨论】:
即使,如果 Json 使用$type
序列化,非泛型 DeserializeObject
重载将(至少对于根对象)不会使用转换器。在这里查看我的问题:***.com/questions/50374760/…
如果指定了 $type,DeserializeObject
返回该类型(而不是例如 JToken),请参见上面链接的另一个问题中的示例
@mike - 你是对的。我从不使用非通用版本,所以我完全忘记了该特定方法如何处理"$type"
。我添加了一个指向更新小提琴的链接以确认行为。【参考方案2】:
将您的 JSON 反序列化为最基本的形式:
Dictionary<string, object> theData= new javascriptSerializer().Deserialize<Dictionary<string, object>>(jsonString);
string baseItemName = (string)theData["baseItem"];
Dictionary<string, object> someNode= (Dictionary<string, object>)theData["something"];
string anything = (string)someNode["anything"];
string nothing = (string)someNode["nothing"];
对Deserialize()
的调用会创建一个Dictionary<string, object>
的树,您可以随意遍历它。
【讨论】:
以上是关于如何将 JSON 反序列化为正确类型的对象,而无需事先定义类型?的主要内容,如果未能解决你的问题,请参考以下文章
无法将 JSON 数组(例如 [1,2,3])反序列化为类型 ' ',因为类型需要 JSON 对象(例如 "name":"value")才能正确反序列化