Wir erstellen eine Anwendung, die unter Xamarin läuft, und wir haben einen gemeinsamen Code, der unter Android, iOS und einfach mono auf einem RaspberryPi verwendet wird. Der Code ist ein Wrapper um den System.Runtime.Serialization.Json.DataContractJsonSerializer und behandelt einfach das Konvertieren einer Klasse in und aus einer JSON-Zeichenfolge. Dieser Code funktioniert unter Windows mit .Net 4.5.2 (das ist die höchste Version, die wir auf dem PI verwenden können, auf dem mono 3.2.8 läuft).System.Runtime.Serialization.Json.DataContractJsonSerializer löst Ausnahme aus Das Argument kann nicht null sein
Das Problem ist, dass wenn wir den Serializer aufrufen, erhalten wir eine System.ArgumentNullException: Argument kann nicht null sein Parametername: rootName.
Nun, unser Objekt ist eine einfache alte Klasse, die keinen "rootName" -Knoten hat, der verdächtig aussieht, als würde es denken, wir seien Xml, nicht Json.
Wir können Newtonsoft nicht verwenden, da es sich beim Umgang mit einer komplexen Klasse um einen "Emitter" handelt, den iOS nicht zulässt, sodass wir auf den .Net Json Serializer zurückgefallen sind.
Alles funktioniert gut, wenn wir Newtonsoft (PI und Android) verwenden, aber das iOS hält uns von dieser Plattform.
Attached ist eine Konsolenanwendung, die das Problem auf dem PI originalgetreu wiedergibt. Es läuft gut in Windows und auch auf unserem Android.
Jede Hilfe wird sehr geschätzt! Vielen Dank.
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
}
'DataContractJsonSerializer' erbt von [' XmlObjectSerializer'] (https://msdn.microsoft.com/en-us/library/system.runtime.serialization.xmlobjectserializer.aspx) unter der Haube, und verwendet ein [spezialisierter XML-Writer] (https://msdn.microsoft.com/en-us/library/system.runtime.serialization.json.jsonreaderwrwrfacturing_methods.aspx) um JSON zu schreiben. Die Ausnahme, die Sie sehen, ist also plausibel. Können Sie die vollständige 'ToString()' -Ausgabe der Ausnahme teilen, einschließlich des Ausnahmetyps, der Nachricht und des Tracebacks? – dbc
hmmm im schlimmsten Fall wirst du Code wie folgt haben: 'if (platform.ios) {benutze .Net Serializer} else {nutze NewtonSoft}', ich hoffe jemand gibt dir Besseres als das :) – niceman
Ich gehe hin Ich schlage vor, Sie versuchen folgendes: 1) Ordnen Sie Ihren Serializer zu, indem Sie 'new DataContractJsonSerializer (model.GetType())' - dh verwenden Sie nicht 'DataContractJsonSerializerSettings'. Löst das das Problem? 2) Weisen Sie die Einstellungen zu, aber tun Sie 'var settings = new DataContractJsonSerializerSettings {SerializeReadOnlyTypes = true, RootName =" root "};'. Führt das Festlegen des ['RootName'] (https://msdn.microsoft.com/en-us/library/system.runtime.serialization.json.dataccontractjsonserializersettings.rootname.aspx) das Problem explizit aus? – dbc