System.Runtime.Serialization.Json.DataContractJsonSerializer抛出异常参数不能为null

时间:2016-07-08 21:17:17

标签: c# json mono

我们正在构建一个在Xamarin下运行的应用程序,我们有一些在Android,iOS和RaspberryPi上运行的普通单声道共享的公共代码。该代码是System.Runtime.Serialization.Json.DataContractJsonSerializer的包装器,只是处理一个类与JSON字符串之间的转换。这个代码在Windows下使用.Net 4.5.2(这是我们可以在运行mono 3.2.8的PI上使用的最高版本)工作正常。

问题是当我们调用序列化程序时,我们得到一个System.ArgumentNullException:Argument不能为null参数名: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

}

1 个答案:

答案 0 :(得分:0)

嗯,基于上面的建议,我在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
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++