代码之家  ›  专栏  ›  技术社区  ›  James Cadd

使用DataContractJsonSerializer将数组值反序列化为.NET属性

  •  19
  • James Cadd  · 技术社区  · 16 年前

    我正在使用Silverlight 4中的DataContractJsonSerializer,并希望反序列化以下JSON:

    {
        "collectionname":"Books",
        "collectionitems": [
                ["12345-67890",201,
                 "Book One"],
                ["09876-54321",45,
                 "Book Two"]
            ]
    }
    

    分为以下几类:

    class BookCollection
    {
      public string collectionname { get; set; }
      public List<Book> collectionitems { get; set; }
    }
    
    class Book
    {
      public string Id { get; set; }
      public int NumberOfPages { get; set; }
      public string Title { get; set; }
    }
    

    扩展DataContractJsonSerializer将“collectionitems”中未命名的第一个数组元素映射到Book类的Id属性,将第二个元素映射到NumberOfPages属性,将最后一个元素映射到Title的正确位置是什么?我无法控制这个实例中的JSON生成,希望该解决方案能够与.NET的Silverlight子集一起工作。如果解决方案也可以执行序列化的反向操作,那就太好了。

    2 回复  |  直到 16 年前
        1
  •  22
  •   Justin Grant    16 年前

    如果这不是Silverlight,你可以用 IDataContractSurrogate 使用 object[] Book IDataContracts代理 DataContractJsonSerializer 使用它的构造函数)在Silverlight中不可用。

    在Silverlight上,这里有一个简单的解决方法。导出 书 从imlpements ICollection<object> 对象[] ,框架将尽职尽责地将其序列化到 i收集<对象> ,然后可以用属性包装。

    最简单(也是最黑客的)就是从 List<object> . 这种简单的黑客攻击的缺点是,用户可以修改基本的列表数据,弄乱你的属性。如果您是这段代码的唯一用户,那就可以了。再多做一点工作,您就可以自己实现 ICollection

    如果你觉得上面的方法太难看的话,我相信还有更优雅的方法来处理这个问题。您可能希望将注意力集中在创建自定义集合类型上,而不是 List<Book> 为了你的 collectionitems 财产。此类型可以包含类型为的字段 List<object[]> (这是JSON中的实际类型),您可以说服序列化程序填充它。然后您的IList实现可以将这些数据挖掘到实际的图书实例中。

    另一行的调查可以尝试强制转换。例如,您可以在 书 和 string[] 序列化是否足够聪明来使用它?我很怀疑,但也许值得一试。

    无论如何,下面是上面提到的从ICollection黑客派生的代码示例。警告:我还没有在Silverlight上验证这些,但是它们应该只使用Silverlight可访问的类型,所以我认为(祈祷吧!)应该可以。

    简单,黑客样本

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.Serialization.Json;
    using System.Runtime.Serialization;
    using System.IO;
    
    [DataContract]
    class BookCollection
    {
        [DataMember(Order=1)]
        public string collectionname { get; set; }
    
        [DataMember(Order = 2)]
        public List<Book> collectionitems { get; set; }
    }
    
    [CollectionDataContract]
    class Book : List<object>
    {
        public string Id { get { return (string)this[0]; } set { this[0] = value; } }
        public int NumberOfPages { get { return (int)this[1]; } set { this[1] = value; } }
        public string Title { get { return (string)this[2]; } set { this[2] = value; } }
    
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(BookCollection));
            string json = "{"
                        + "\"collectionname\":\"Books\","
                        + "\"collectionitems\": [ "
                                + "[\"12345-67890\",201,\"Book One\"],"
                                + "[\"09876-54321\",45,\"Book Two\"]"
                            + "]"
                        + "}";
    
            using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
            {
                BookCollection obj = ser.ReadObject(ms) as BookCollection;
                using (MemoryStream ms2 = new MemoryStream())
                {
                    ser.WriteObject(ms2, obj);
                    string serializedJson = Encoding.UTF8.GetString(ms2.GetBuffer(), 0,  (int)ms2.Length);
                }
            }
        }
    }
    

    更硬,稍不粗糙的样本

    下面是第二个示例,展示了ICollection的手动实现,它阻止用户访问集合--它支持调用 Add() 3次(反序列化期间),但不允许通过 ICollection<T>

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.Serialization.Json;
    using System.Runtime.Serialization;
    using System.IO;
    using System.Diagnostics;
    using System.ComponentModel;
    
    [DataContract]
    class BookCollection
    {
        [DataMember(Order=1)]
        public string collectionname { get; set; }
    
        [DataMember(Order = 2)]
        public List<Book> collectionitems { get; set; }
    }
    
    [CollectionDataContract]
    class Book : ICollection<object>
    {
        public string Id { get; set; }
        public int NumberOfPages { get; set; }
        public string Title { get; set; }
    
        // code below here is only used for serialization/deserialization
    
        // keeps track of how many properties have been initialized
        [EditorBrowsable(EditorBrowsableState.Never)]
        private int counter = 0;
    
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void Add(object item)
        {
            switch (++counter)
            {
                case 1:
                    Id = (string)item;
                    break;
                case 2:
                    NumberOfPages = (int)item;
                    break;
                case 3:
                    Title = (string)item;
                    break;
                default:
                    throw new NotSupportedException();
            }
        }
    
        [EditorBrowsable(EditorBrowsableState.Never)]
        IEnumerator<object> System.Collections.Generic.IEnumerable<object>.GetEnumerator() 
        {
            return new List<object> { Id, NumberOfPages, Title }.GetEnumerator();
        }
    
        [EditorBrowsable(EditorBrowsableState.Never)]
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
        {
            return new object[] { Id, NumberOfPages, Title }.GetEnumerator();
        }
    
        [EditorBrowsable(EditorBrowsableState.Never)]
        int System.Collections.Generic.ICollection<object>.Count 
        { 
            get { return 3; } 
        }
    
        [EditorBrowsable(EditorBrowsableState.Never)]
        bool System.Collections.Generic.ICollection<object>.IsReadOnly 
        { get { throw new NotSupportedException(); } }
    
        [EditorBrowsable(EditorBrowsableState.Never)]
        void System.Collections.Generic.ICollection<object>.Clear() 
        { throw new NotSupportedException(); }
    
        [EditorBrowsable(EditorBrowsableState.Never)]
        bool System.Collections.Generic.ICollection<object>.Contains(object item) 
        { throw new NotSupportedException(); }
    
        [EditorBrowsable(EditorBrowsableState.Never)]
        void System.Collections.Generic.ICollection<object>.CopyTo(object[] array, int arrayIndex) 
        { throw new NotSupportedException(); }
    
        [EditorBrowsable(EditorBrowsableState.Never)]
        bool System.Collections.Generic.ICollection<object>.Remove(object item) 
        { throw new NotSupportedException(); }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(BookCollection));
            string json = "{"
                        + "\"collectionname\":\"Books\","
                        + "\"collectionitems\": [ "
                                + "[\"12345-67890\",201,\"Book One\"],"
                                + "[\"09876-54321\",45,\"Book Two\"]"
                            + "]"
                        + "}";
    
            using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
            {
                BookCollection obj = ser.ReadObject(ms) as BookCollection;
                using (MemoryStream ms2 = new MemoryStream())
                {
                    ser.WriteObject(ms2, obj);
                    string serializedJson = Encoding.UTF8.GetString(ms2.GetBuffer(), 0,  (int)ms2.Length);
                }
            }
        }
    }
    

    顺便说一句,我第一次读你的问题时跳过了重要的Silverlight要求。哎呀!不管怎样,如果不使用Silverlight,下面是我为这种情况编写的解决方案——它更简单,我不妨把它保存在这里,以供以后的Google用户使用。

    (在常规的.NET框架上,而不是Silverlight上)您正在寻找的魔法是 IDataContractSurrogate . 在序列化/反序列化时,如果要用一种类型替换另一种类型,请实现此接口。在你的情况下,你必须用 对象[] 对于 书

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.Serialization.Json;
    using System.Runtime.Serialization;
    using System.IO;
    using System.Collections.ObjectModel;
    
    [DataContract]
    class BookCollection
    {
        [DataMember(Order=1)]
        public string collectionname { get; set; }
    
        [DataMember(Order = 2)]
        public List<Book> collectionitems { get; set; }
    }
    
    class Book 
    { 
      public string Id { get; set; } 
      public int NumberOfPages { get; set; } 
      public string Title { get; set; } 
    } 
    
    // A type surrogate substitutes object[] for Book when serializing/deserializing.
    class BookTypeSurrogate : IDataContractSurrogate
    {
        public Type GetDataContractType(Type type)
        {
            // "Book" will be serialized as an object array
            // This method is called during serialization, deserialization, and schema export. 
            if (typeof(Book).IsAssignableFrom(type))
            {
                return typeof(object[]);
            }
            return type;
        }
        public object GetObjectToSerialize(object obj, Type targetType)
        {
            // This method is called on serialization.
            if (obj is Book)
            {
                Book book = (Book) obj;
                return new object[] { book.Id, book.NumberOfPages, book.Title };
            }
            return obj;
        }
        public object GetDeserializedObject(object obj, Type targetType)
        {
            // This method is called on deserialization.
            if (obj is object[])
            {
                object[] arr = (object[])obj;
                Book book = new Book { Id = (string)arr[0], NumberOfPages = (int)arr[1], Title = (string)arr[2] };
                return book;
            }
            return obj;
        }
        public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
        {
            return null; // not used
        }
        public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
        {
            return typeDeclaration; // Not used
        }
        public object GetCustomDataToExport(Type clrType, Type dataContractType)
        {
            return null; // not used
        }
        public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
        {
            return null; // not used
        }
        public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
        {
            return; // not used
        }
    }
    
    
    class Program
    {
        static void Main(string[] args)
        {
            DataContractJsonSerializer ser  =
                new DataContractJsonSerializer(
                    typeof(BookCollection), 
                    new List<Type>(),        /* knownTypes */
                    int.MaxValue,            /* maxItemsInObjectGraph */ 
                    false,                   /* ignoreExtensionDataObject */
                    new BookTypeSurrogate(),  /* dataContractSurrogate */
                    false                    /* alwaysEmitTypeInformation */
                    );
            string json = "{"
                        + "\"collectionname\":\"Books\","
                        + "\"collectionitems\": [ "
                                + "[\"12345-67890\",201,\"Book One\"],"
                                + "[\"09876-54321\",45,\"Book Two\"]"
                            + "]"
                        + "}";
    
            using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
            {
                BookCollection obj = ser.ReadObject(ms) as BookCollection;
                using (MemoryStream ms2 = new MemoryStream())
                {
                    ser.WriteObject(ms2, obj);
                    string serializedJson = Encoding.UTF8.GetString(ms2.GetBuffer(), 0,  (int)ms2.Length);
                }
            }
        }
    }
    
        2
  •  1
  •   Oleg    16 年前

    {
      "collectionname":"Books",
      "collectionitems":[
        {"book":["12345-67890",201,"Book One"]},
        {"book":["09876-54321",45,"Book Two"]}
      ]
    }
    

    小型控制台应用程序的对应代码:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Security.Permissions;
    
    namespace DataContractJsonSerializer {
        [DataContract]
        class BookCollection {
            [DataMember (Order = 0)]
            public string collectionname { get; set; }
            [DataMember (Order = 1)]
            public List<Book> collectionitems { get; set; }
        }
    
        [Serializable]
        [KnownType (typeof (object[]))]
        class Book: ISerializable {
            public string Id { get; set; }
            public int NumberOfPages { get; set; }
            public string Title { get; set; }
    
            public Book () { }
    
            [SecurityPermissionAttribute (SecurityAction.Demand, Flags = SecurityPermissionFlag.SerializationFormatter)]
            protected Book (SerializationInfo info, StreamingContext context) {
                // called by DataContractJsonSerializer.ReadObject
                Object[] ar = (Object[]) info.GetValue ("book", typeof (object[]));
    
                this.Id = (string)ar[0];
                this.NumberOfPages = (int)ar[1];
                this.Title = (string)ar[2];
            }
    
            [SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)]
            public void GetObjectData (SerializationInfo info, StreamingContext context) {
                // called by DataContractJsonSerializer.WriteObject
                object[] ar = new object[] { (object)this.Id, (object)this.NumberOfPages, (object)this.Title };
                info.AddValue ("book", ar);
            }
        }
    
        class Program {
            static readonly string testJSONdata = "{\"collectionname\":\"Books\",\"collectionitems\":[{\"book\":[\"12345-67890\",201,\"Book One\"]},{\"book\":[\"09876-54321\",45,\"Book Two\"]}]}";
    
            static void Main (string[] args) {
                BookCollection test = new BookCollection () {
                    collectionname = "Books",
                    collectionitems = new List<Book> {
                        new Book() { Id = "12345-67890", NumberOfPages = 201, Title = "Book One"},
                        new Book() { Id = "09876-54321", NumberOfPages = 45, Title = "Book Two"},
                    }
                };
    
                MemoryStream memoryStream = new MemoryStream ();
                System.Runtime.Serialization.Json.DataContractJsonSerializer ser =
                    new System.Runtime.Serialization.Json.DataContractJsonSerializer (typeof (BookCollection));
                memoryStream.Position = 0;
                ser.WriteObject (memoryStream, test);
    
                memoryStream.Flush();
                memoryStream.Position = 0;
                StreamReader sr = new StreamReader(memoryStream);
                string str = sr.ReadToEnd ();
                Console.WriteLine ("The result of custom serialization:");
                Console.WriteLine (str);
    
                if (String.Compare (testJSONdata, str, StringComparison.Ordinal) != 0) {
                    Console.WriteLine ("Error in serialization: unexpected results.");
                        return;
                }
    
                byte[] jsonDataAsBytes = System.Text.Encoding.GetEncoding ("iso-8859-1").GetBytes (testJSONdata);
                MemoryStream stream = new MemoryStream (jsonDataAsBytes);
                stream.Position = 0;
                BookCollection p2 = (BookCollection)ser.ReadObject (stream);
            }
        }
    }