System.Runtime.Serialization.Json.DataContractJsonSerializer
本文关键字:DataContractJsonSerializer Json Serialization Runtime System | 更新日期: 2023-09-27 17:56:08
我们正在构建一个在Xamarin下运行的应用程序,我们有一些通用代码在Android,iOS和RaspberryPi上运行的普通单声道之间共享。 该代码是 System.Runtime.Serialization.Json.DataContractJsonSerializer 的包装器,只是处理将类与 JSON 字符串之间的转换。 此代码在使用 .Net 4.5.2 的 Windows 下运行良好(这是我们可以在运行单声道 3.2.8 的 PI 上使用的最高版本)。
问题是,当我们调用序列化程序时,我们得到一个 System.ArgumentNullException: 参数不能为空 参数名称:rootName。
好吧,我们的对象是一个普通的旧类,它没有"rootName"节点,看起来很可疑,就像它认为我们是Xml,而不是Json。
我们不能使用Newtonsoft,因为在处理复杂类时,它会调用iOS不允许的"发射器",因此我们退回到.Net Json序列化程序。
当我们使用Newtonsoft(PI和Android)时,一切正常,但是iOS使我们远离该平台。
附件是一个控制台应用程序,它忠实地重现了 PI 上的问题。 它在Windows和我们的Android上运行良好。
任何帮助将不胜感激! 谢谢。
using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
namespace TestSerialization
{
#region Program
/// <summary>
/// Class Program - this is the entry point.
/// </summary>
class Program
{
#region Main
/// <summary>
/// Defines the entry point of the application.
/// </summary>
/// <param name="args">The arguments.</param>
static void Main(string[] args)
{
TestThis();
Console.ReadLine();
}
#endregion
#region TestThis
/// <summary>
/// Tests the various scenarios
/// </summary>
private static void TestThis()
{
try
{
//
// Build the test objects
//
ClassWithNoConstructor classWithNoConstructor = new ClassWithNoConstructor { Int01 = 1, Int02 = 2, String01 = "hello01", String02 = "world02", OtherClass01 = new EmbeddedClass { OC01 = "OCString01", OC02 = "OCString02" } };
ClassWithConstructorForStrings classWithConstructorForStrings = new ClassWithConstructorForStrings("hello03", "world04") { Int01 = 3, Int02 = 4, OtherClass01 = new EmbeddedClass { OC01 = "OCString03", OC02 = "OCString04" } };
ClassWithEmptyConstructor classWithEmptyConstructor = new ClassWithEmptyConstructor();
classWithEmptyConstructor.Int01 = 5;
classWithEmptyConstructor.Int02 = 6;
classWithEmptyConstructor.String01 = "hello05";
classWithEmptyConstructor.String02 = "world06";
classWithEmptyConstructor.OtherClass01 = new EmbeddedClass();
classWithEmptyConstructor.OtherClass01.OC01 = "OCString05";
classWithEmptyConstructor.OtherClass01.OC02 = "OCString06";
try
{
string json = JsonHelper.Serialize(classWithNoConstructor, false);
Console.WriteLine(">>> {0} <<<",classWithNoConstructor.GetType().Name);
Console.WriteLine("NewtonSoft : {0}", json);
json = JsonHelper.Serialize(classWithNoConstructor, true);
Console.WriteLine("DataContract: {0}", json);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
try
{
string json = JsonHelper.Serialize(classWithConstructorForStrings, false);
Console.WriteLine(">>> {0} <<<", classWithConstructorForStrings.GetType().Name);
Console.WriteLine("NewtonSoft : {0}", json);
json = JsonHelper.Serialize(classWithConstructorForStrings, true);
Console.WriteLine("DataContract: {0}", json);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
try
{
string json = JsonHelper.Serialize(classWithEmptyConstructor, false);
Console.WriteLine(">>> {0} <<<", classWithEmptyConstructor.GetType().Name);
Console.WriteLine("NewtonSoft : {0}", json);
json = JsonHelper.Serialize(classWithEmptyConstructor, true);
Console.WriteLine("DataContract: {0}", json);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.WriteLine();
ShowComparison<ClassWithNoConstructor>(classWithNoConstructor);
ShowComparison<ClassWithConstructorForStrings>(classWithConstructorForStrings);
ShowComparison<ClassWithEmptyConstructor>(classWithEmptyConstructor);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
#endregion
#region ShowComparison
/// <summary>
/// Shows the comparison between Newtonsoft and the Data Contract serializer.
/// </summary>
/// <typeparam name="T">Type of the data you are comparing</typeparam>
/// <param name="data">The data object you wish to compare.</param>
private static void ShowComparison<T>(object data)
{
//
// Show them as strings, deserialize them and show if they deserialized correctly
//
StringBuilder sb = new StringBuilder();
bool isTheSameNS = false;
bool isTheSameDC = false;
Type typeOfTheData = data.GetType();
bool DataContractFlag = false;
string jsonNS = JsonHelper.Serialize(data, DataContractFlag);
T deserializedNS = JsonHelper.DeserializeObject<T>(jsonNS, DataContractFlag);
DataContractFlag = true;
string jsonDC = JsonHelper.Serialize(data, DataContractFlag);
T deserializedDC = JsonHelper.DeserializeObject<T>(jsonDC, DataContractFlag);
if (typeOfTheData == typeof(ClassWithNoConstructor))
{
isTheSameNS = ((ClassWithNoConstructor)data).IsEqualTo(deserializedNS as ClassWithNoConstructor);
isTheSameDC = ((ClassWithNoConstructor)data).IsEqualTo(deserializedDC as ClassWithNoConstructor);
}
else if (typeOfTheData == typeof(ClassWithConstructorForStrings))
{
isTheSameNS = ((ClassWithConstructorForStrings)data).IsEqualTo(deserializedNS as ClassWithConstructorForStrings);
isTheSameDC = ((ClassWithConstructorForStrings)data).IsEqualTo(deserializedDC as ClassWithConstructorForStrings);
}
else if (typeOfTheData == typeof(ClassWithEmptyConstructor))
{
isTheSameNS = ((ClassWithEmptyConstructor)data).IsEqualTo(deserializedNS as ClassWithEmptyConstructor);
isTheSameDC = ((ClassWithEmptyConstructor)data).IsEqualTo(deserializedDC as ClassWithEmptyConstructor);
}
sb.AppendLine(string.Format("======== {0} ======================================================", typeOfTheData.Name));
sb.AppendLine(string.Format("++++ NewtonSoft +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"));
sb.AppendLine(string.Format("Json: {0}", jsonNS));
sb.AppendLine(string.Format("Does Serialized object match original? {0}", isTheSameNS));
sb.AppendLine("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
sb.AppendLine(string.Format("++++ DataContract +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"));
sb.AppendLine(string.Format("Json: {0}", jsonDC));
sb.AppendLine(string.Format("Does Serialized object match original? {0}", isTheSameDC));
sb.AppendLine("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
sb.AppendLine(string.Format("Does DataContract Json Match Newtownsoft Json? {0}", jsonNS.Equals(jsonDC)));
sb.AppendLine("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
Console.WriteLine(sb.ToString());
sb.Clear();
}
#endregion
}
#endregion
#region JsonHelper
/// <summary>
/// Class JsonHelper. Wraps different ways to convert models to and from Json
/// </summary>
public static class JsonHelper
{
#region Serialize
/// <summary>
/// Serializes the specified model.
/// </summary>
/// <param name="model">The model that you wish to serialize.</param>
/// <param name="useDataContract">if set to <c>true</c> use the DataContractSerializer.</param>
/// <returns>System.String.</returns>
public static string Serialize(object model, bool useDataContract = false)
{
if (useDataContract)
{
string result = string.Empty;
DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings { SerializeReadOnlyTypes = true };
DataContractJsonSerializer js = new DataContractJsonSerializer(model.GetType(), settings);
using (MemoryStream ms = new MemoryStream())
{
js.WriteObject(ms, model);
ms.Position = 0;
using (StreamReader sr = new StreamReader(ms))
{
result = sr.ReadToEnd();
}
}
return result;
}
else
{
return Newtonsoft.Json.JsonConvert.SerializeObject(model);
}
}
#endregion
#region DeserializeObject
/// <summary>
/// Deserializes the object.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="json">The json string you wish to deserialize.</param>
/// <param name="useDataContract">if set to <c>true</c> use the DataContractSerializer.</param>
/// <returns>T.</returns>
public static T DeserializeObject<T>(string json, bool useDataContract = false)
{
if (useDataContract)
{
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
T result = (T)js.ReadObject(ms);
return result;
}
}
else
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
}
}
#endregion
}
#endregion
#region ClassWithEmptyConstructor
/// <summary>
/// Class ClassWithEmptyConstructor - Just another test case to be sure a "default empty" contstructor still works.
/// </summary>
public class ClassWithEmptyConstructor
{
public ClassWithEmptyConstructor() { }
public int Int01 { get; set; }
public int Int02 { get; set; }
public string String01 { get; set; }
public string String02 { get; set; }
public string ReadOnly { get { return "ReadOnly03"; } }
public EmbeddedClass OtherClass01 { get; set; }
public bool IsEqualTo(ClassWithEmptyConstructor other)
{
if ((other.Int01 == this.Int01)
&& (other.Int02 == this.Int02)
&& (other.String01 == this.String01)
&& (other.String02 == this.String02)
&& (other.OtherClass01.Equals(this.OtherClass01)))
return true;
else
return false;
}
}
#endregion
#region ClassWithNoConstructor
/// <summary>
/// Class ClassWithNoConstructor - just a plain class to be initialized by the calling program.
/// </summary>
public class ClassWithNoConstructor
{
public int Int01 { get; set; }
public int Int02 { get; set; }
public string String01 { get; set; }
public string String02 { get; set; }
public string ReadOnly { get { return "ReadOnly01"; } }
public EmbeddedClass OtherClass01 { get; set; }
public bool IsEqualTo(ClassWithNoConstructor other)
{
if ((other.Int01 == this.Int01)
&& (other.Int02 == this.Int02)
&& (other.String01 == this.String01)
&& (other.String02 == this.String02)
&& (other.OtherClass01.Equals(this.OtherClass01)))
return true;
else
return false;
}
}
#endregion
#region ClassWithConstructorForStrings
/// <summary>
/// Class ClassWithConstructorForStrings - a data class with a constructor that takes two strings
/// and also an empty constructor because the DataContract serializer requires it. The Newtonsoft
/// serializer does NOT require the empty constructor
/// </summary>
public class ClassWithConstructorForStrings
{
public ClassWithConstructorForStrings() { }
public ClassWithConstructorForStrings(string s01, string s02) { String01 = s01; String02 = s02; }
public int Int01 { get; set; }
public int Int02 { get; set; }
public string String01 { get; set; }
public string String02 { get; set; }
public string ReadOnly { get { return "ReadOnly02"; } }
public EmbeddedClass OtherClass01 { get; set; }
public bool IsEqualTo(ClassWithConstructorForStrings other)
{
if ((other.Int01 == this.Int01)
&& (other.Int02 == this.Int02)
&& (other.String01 == this.String01)
&& (other.String02 == this.String02)
&& (other.OtherClass01.Equals(this.OtherClass01)))
return true;
else
return false;
}
}
#endregion
#region EmbeddedClass
/// <summary>
/// Class EmbeddedClass - used to test a complex class serialization
/// This fails in NewtonSoft / Xamarin / iOS because it calls a CodeEmitter which apparently iOS does not allow.
/// </summary>
public class EmbeddedClass
{
public string OC01 { get; set; }
public string OC02 { get; set; }
public override bool Equals(object obj)
{
EmbeddedClass other = (EmbeddedClass)obj;
if (other.OC01 == OC01 && other.OC02 == OC02)
return true;
else
return false;
}
}
#endregion
}
好吧,根据上面的建议,我在 Json 助手中尝试了以下内容:
#region Serialize
/// <summary>
/// Serializes the specified model.
/// </summary>
/// <param name="model">The model that you wish to serialize.</param>
/// <param name="useDataContract">if set to <c>true</c> use the DataContractSerializer.</param>
/// <returns>System.String.</returns>
public static string Serialize(object model, bool useDataContract = false)
{
if (useDataContract)
{
string result = string.Empty;
DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings { SerializeReadOnlyTypes = true, RootName = "root", MaxItemsInObjectGraph = 1000 };
DataContractJsonSerializer js = new DataContractJsonSerializer(model.GetType(), settings);
using (MemoryStream ms = new MemoryStream())
{
js.WriteObject(ms, model);
ms.Position = 0;
using (StreamReader sr = new StreamReader(ms))
{
result = sr.ReadToEnd();
}
}
return result;
}
else
{
return Newtonsoft.Json.JsonConvert.SerializeObject(model);
}
}
#endregion
这似乎已经解决了问题。 对于好奇的人来说,附上之前要求的来自PI的两个堆栈跟踪(以及实际工作的最终版本)。 谢谢大家!
=========== Stack Trace #1 =====================
Code:
DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings { SerializeReadOnlyTypes = true };
DataContractJsonSerializer js = new DataContractJsonSerializer(model.GetType(), settings);
System.ArgumentNullException: Argument cannot be null.
Parameter name: rootName
at System.Runtime.Serialization.Json.DataContractJsonSerializer..ctor (System.Type type, System.String rootName, IEnumerable`1 knownTypes, Int32 maxItemsInObjectGraph, Boolean ignoreExtensionDataObject, Boolean alwaysEmitTypeInformation) [0x00000] in <filename unknown>:0
at System.Runtime.Serialization.Json.DataContractJsonSerializer..ctor (System.Type type, System.String rootName, IEnumerable`1 knownTypes, Int32 maxItemsInObjectGraph, Boolean ignoreExtensionDataObject, IDataContractSurrogate dataContractSurrogate, Boolean alwaysEmitTypeInformation) [0x00000] in <filename unknown>:0
at System.Runtime.Serialization.Json.DataContractJsonSerializer..ctor (System.Type type, System.Runtime.Serialization.Json.DataContractJsonSerializerSettings settings) [0x00000] in <filename unknown>:0
at TestSerialization.JsonHelper.Serialize (System.Object model, Boolean useDataContract) [0x00000] in <filename unknown>:0
at TestSerialization.Program.TestThis () [0x00000] in <filename unknown>:0
=========== Stack Trace #2 =====================
Code:
DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings { SerializeReadOnlyTypes = true, RootName="root" };
DataContractJsonSerializer js = new DataContractJsonSerializer(model.GetType(), settings);
System.Runtime.Serialization.SerializationException: There was an error during serialization for object of type TestSerialization.ClassWithNoConstructor ---> System.Runtime.Serialization.SerializationException: The object graph exceeded the maximum object count '0' specified in the serializer
at System.Runtime.Serialization.Json.JsonSerializationWriter.WriteObjectContent (System.Object graph, Boolean top, Boolean outputTypeName) [0x00000] in <filename unknown>:0
at System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObjectContent (System.Xml.XmlWriter writer, System.Object graph) [0x00000] in <filename unknown>:0
at System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject (System.Xml.XmlWriter writer, System.Object graph) [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject (System.Xml.XmlWriter writer, System.Object graph) [0x00000] in <filename unknown>:0
at System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject (System.Xml.XmlDictionaryWriter writer, System.Object graph) [0x00000] in <filename unknown>:0
at System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject (System.IO.Stream stream, System.Object graph) [0x00000] in <filename unknown>:0
at TestSerialization.JsonHelper.Serialize (System.Object model, Boolean useDataContract) [0x00000] in <filename unknown>:0
at TestSerialization.Program.TestThis () [0x00000] in <filename unknown>:0
=========== Stack Trace #3 =====================
Code:
DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings { SerializeReadOnlyTypes = true, RootName = "root", MaxItemsInObjectGraph = 1000 };
DataContractJsonSerializer js = new DataContractJsonSerializer(model.GetType(), settings);
>>> ClassWithNoConstructor <<<
NewtonSoft : {"Int01":1,"Int02":2,"String01":"hello01","String02":"world02","ReadOnly":"ReadOnly01","OtherClass01":{"OC01":"OCString01","OC02":"OCString02"}}
DataContract: {"Int01":1,"Int02":2,"OtherClass01":{"OC01":"OCString01","OC02":"OCString02"},"String01":"hello01","String02":"world02"}
>>> ClassWithConstructorForStrings <<<
NewtonSoft : {"Int01":3,"Int02":4,"String01":"hello03","String02":"world04","ReadOnly":"ReadOnly02","OtherClass01":{"OC01":"OCString03","OC02":"OCString04"}}
DataContract: {"Int01":3,"Int02":4,"OtherClass01":{"OC01":"OCString03","OC02":"OCString04"},"String01":"hello03","String02":"world04"}
>>> ClassWithEmptyConstructor <<<
NewtonSoft : {"Int01":5,"Int02":6,"String01":"hello05","String02":"world06","ReadOnly":"ReadOnly03","OtherClass01":{"OC01":"OCString05","OC02":"OCString06"}}
DataContract: {"Int01":5,"Int02":6,"OtherClass01":{"OC01":"OCString05","OC02":"OCString06"},"String01":"hello05","String02":"world06"}
======== ClassWithNoConstructor ======================================================
++++ NewtonSoft +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Json: {"Int01":1,"Int02":2,"String01":"hello01","String02":"world02","ReadOnly":"ReadOnly01","OtherClass01":{"OC01":"OCString01","OC02":"OCString02"}}
Does Serialized object match original? True
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++ DataContract +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Json: {"Int01":1,"Int02":2,"OtherClass01":{"OC01":"OCString01","OC02":"OCString02"},"String01":"hello01","String02":"world02"}
Does Serialized object match original? True
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Does DataContract Json Match Newtownsoft Json? False
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
======== ClassWithConstructorForStrings ======================================================
++++ NewtonSoft +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Json: {"Int01":3,"Int02":4,"String01":"hello03","String02":"world04","ReadOnly":"ReadOnly02","OtherClass01":{"OC01":"OCString03","OC02":"OCString04"}}
Does Serialized object match original? True
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++ DataContract +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Json: {"Int01":3,"Int02":4,"OtherClass01":{"OC01":"OCString03","OC02":"OCString04"},"String01":"hello03","String02":"world04"}
Does Serialized object match original? True
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Does DataContract Json Match Newtownsoft Json? False
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
======== ClassWithEmptyConstructor ======================================================
++++ NewtonSoft +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Json: {"Int01":5,"Int02":6,"String01":"hello05","String02":"world06","ReadOnly":"ReadOnly03","OtherClass01":{"OC01":"OCString05","OC02":"OCString06"}}
Does Serialized object match original? True
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++ DataContract +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Json: {"Int01":5,"Int02":6,"OtherClass01":{"OC01":"OCString05","OC02":"OCString06"},"String01":"hello05","String02":"world06"}
Does Serialized object match original? True
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Does DataContract Json Match Newtownsoft Json? False
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++