Ответ 1
Если это не Silverlight, вы можете использовать IDataContractSurrogate
, чтобы использовать object[]
(что фактически присутствует в вашем JSON) вместо Book
при сериализации/десериализации. К сожалению, IDataContractSurrogate
(и перегрузки DataContractJsonSerializer
, которые его используют) недоступны в Silverlight.
В Silverlight, здесь хакерский, но простой обходной путь. Выведите класс Book
из типа, который imlpements ICollection<object>
. Поскольку тип в вашем сериализованном JSON object[]
, структура будет покорно сериализовать его в ваш ICollection<object>
, который, в свою очередь, можно обернуть вашими свойствами.
Самый простой (и самый хакерский) - это просто результат List<object>
. Недостатком этого легкого взлома является то, что пользователи могут изменять основные данные списка и испортить ваши свойства. Если вы единственный пользователь этого кода, возможно, все в порядке. Немного поработав, вы можете выполнить собственную реализацию ICollection
и разрешить только достаточные методы для работы сериализации и бросать исключения для остальных. Я включил примеры кода для обоих подходов ниже.
Если вышеупомянутые хаки слишком уродливы для вас, я уверен, что есть более грациозные способы справиться с этим. Вероятно, вы захотите сосредоточить свое внимание на создании пользовательского типа коллекции вместо List<Book>
для вашего свойства collectionitems
. Этот тип может содержать поле типа List<object[]>
(которое является фактическим типом в вашем JSON), которое вы могли бы убедить сериализатор заполнить. Тогда ваша реализация IList могла бы разминировать эти данные в реальных экземплярах книги.
Другая строка исследования может попробовать кастинг. Например, можно ли реализовать неявное преобразование типов между Book
и 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>
. Методы ICollection раскрываются с использованием явной реализации интерфейса, и есть атрибуты этих методов, чтобы скрыть их от intellisense, что должно еще больше уменьшить коэффициент взлома. Но, как вы видите, это намного больше кода.
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);
}
}
}
}
Кстати, в первый раз, когда я прочитал ваш quesiton, я пропустил важное требование Silverlight. К сожалению! Во всяком случае, если не использовать Silverlight, вот решение, которое я закодировал для этого случая - это намного проще, и я мог бы также сохранить его здесь для любых Googlers, которые придут позже.
(на обычной платформе .NET, а не Silverlight) магия, которую вы ищете, IDataContractSurrogate. Внедрите этот интерфейс, если вы хотите подменить один тип для другого типа при сериализации/десериализации. В вашем случае вы замените object[]
на Book
.
Вот какой код показывает, как это работает:
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);
}
}
}
}