2015-03-18 3 views
4

Мне нужно сериализовать множество разных объектов с помощью Json.NET. Я действительно не контролирую предоставляемые объекты, поэтому я обобщаю и де-сериализую с помощью TypeNameHandling.All.Как я могу в целом десериализовать PropertyInfo с помощью Json.NET?

Однако некоторые из этих объектов нельзя де-сериализовать. В частности, я получаю некоторые типы System.Reflection.RuntimePropertyInfo. Я бы хотел обработать их стандартизованным образом, поскольку я не знал о типе цели во время де-сериализации. Меня также не волнует, если тип выходного объекта верен.

Я попытался ввести CustomCreationConverter в PropertyInfo, который определен в JsonSerializerSettings. Однако, несмотря на то, что CanConvert() возвращает true, ReadJson() CustomCreationConverter никогда не используется.

Конечный результат такой же, как если бы я никогда не использовал CustomCreationConverter:

ISerializable типа «System.Reflection.RuntimePropertyInfo» не иметь действительный конструктор. Чтобы правильно реализовать ISerializable, должен присутствовать конструктор , который принимает параметры SerializationInfo и StreamingContext .

Мне нужен CustomCreationConverter для обработки ReadJson, чтобы я мог вручную самостоятельно искать PropertyInfo.

После того, как выяснилось, что конвертеры, которые я добавляю в JsonSerializerSettings, вообще не используются. Если я использую перегрузку DeserializeObject, которая включает в себя тип и коллекцию JsonConverter, конвертер будет использоваться. Я не уверен, что для конвертеров, поставляемых в JsonSerializerSettings, используются, но я бы ожидал, что они будут работать так, как я намереваюсь в этой ситуации.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using System.Runtime.Serialization.Formatters; 
using System.Text; 
using System.Threading.Tasks; 
using Newtonsoft.Json; 
using Newtonsoft.Json.Converters; 
using Newtonsoft.Json.Linq; 

namespace Json 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var jsonSerializerSettings = new JsonSerializerSettings() 
      { 
       TypeNameHandling = TypeNameHandling.All, 
       TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple, 
       Converters = new JsonConverter[] { new PropertyInfoConverter(), }, 
      }; 
      var propertyInfo = typeof(Test).GetProperty("Name"); 

      var serialized = JsonConvert.SerializeObject(propertyInfo, jsonSerializerSettings); 
      var deserialized = JsonConvert.DeserializeObject(serialized, jsonSerializerSettings); 
     } 
    } 

    public class Test 
    { 
     public string Name { get; set; } 
    } 

    public class PropertyInfoConverter : CustomCreationConverter<PropertyInfo> 
    { 
     public override bool CanConvert(Type objectType) 
     { 
      return typeof(PropertyInfo).IsAssignableFrom(objectType); 
     } 

     public override PropertyInfo Create(Type objectType) 
     { 
      return null; 
     } 

     public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
     { 
      return null; // This is never invoked, but is where I would attempt to find the PropertyInfo via Reflection searching. 
     } 
    } 
} 
+2

Зачем вам нужно сериализовать весь объект PropertyInfo? Недостаточно ли имени свойства? Это может быть сложно и сложно, так как PropertyInfo ссылается на Type и Type имеет ссылку на Assembly и Assembly, имеет множество разных ссылок на другие объекты ... –

+0

В частности, это для ввода нового кэширующего провайдера в очень старую устаревшую систему.Сериализация - это требование механизма кэширования нового провайдера. Поставщик должен повторить действия предыдущего провайдера дословно в отношении доступа к кэшу. –

+0

Возможно, вы должны упомянуть об исключении 'ISerializable type 'System.Reflection.RuntimePropertyInfo' не имеет допустимого конструктора. Чтобы правильно реализовать ISerializable, должен присутствовать конструктор, который принимает параметры SerializationInfo и StreamingContext. Путь '', строка 1, позиция 287.' –

ответ

1

Предполагая API вам нужно, чтобы соответствовать необходимо ввести/вывести PropertyInfo объект, который вы можете вырезать конвертер и использовать новый класс для сохранения только информации, необходимой для воссоздания объекта.

private class PropertyInfoData 
{ 
    public string TypeName 
    { 
     get; 
     set; 
    } 

    public string PropertyName 
    { 
     get; 
     set; 
    } 

    public static PropertyInfoData FromProperty(PropertyInfo p) 
    { 
     return new PropertyInfoData() 
     { 
      TypeName = p.DeclaringType.AssemblyQualifiedName, 
      PropertyName = p.Name, 
     }; 
    } 

    public PropertyInfo ToProperty() 
    { 
     return Type.GetType(this.TypeName).GetProperty(this.PropertyName); 
    } 
} 

Тогда вы можете де/сериализации это так:

var jsonSerializerSettings = new JsonSerializerSettings() 
{ 
    TypeNameHandling = TypeNameHandling.All, 
    TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple, 
    //Converters = new JsonConverter[] { new PropertyInfoConverter(), }, 
}; 
var propertyInfo = typeof(Test).GetProperty("Name"); 

var serialized = JsonConvert.SerializeObject(PropertyInfoData.FromProperty(propertyInfo), jsonSerializerSettings); 
var deserialized = ((PropertyInfoData)JsonConvert.DeserializeObject(serialized, jsonSerializerSettings)).ToProperty(); 

Вот очень сырой (метод чтения не очень надежен) Пример использования преобразователя, а также:

public class PropertyInfoConverter : JsonConverter 
{ 
public override bool CanWrite 
{ 
    get 
    { 
     return false; 
    } 
} 

public override bool CanConvert(Type objectType) 
{ 
    return typeof(PropertyInfo).IsAssignableFrom(objectType); 
} 

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
{ 
    string propertyName = null; 
    string assemblyName = null; 
    string typeName = null; 

    while (reader.Read()) 
    { 
     if (reader.TokenType == JsonToken.PropertyName) 
     { 
      string value = reader.Value.ToString(); 

      switch (reader.Value.ToString()) 
      { 
       case "Name": 
        if (reader.Read()) 
        { 
         propertyName = reader.Value.ToString(); 
        } 
        break; 
       case "AssemblyName": 
        if (reader.Read()) 
        { 
         assemblyName = reader.Value.ToString(); 
        } 
        break; 
       case "ClassName": 
        if (reader.Read()) 
        { 
         typeName = reader.Value.ToString(); 
        } 
        break; 
      } 
     }  
    } 

    return Type.GetType(typeName + ", " + assemblyName).GetProperty(propertyName); 
} 

/// <inheritdoc /> 
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
{ 
    // When the property "CanWrite" returns false this method is never expected to be called. 
    throw new NotImplementedException(); 
} 

Примечание. Чтобы заставить десериализованный метод использовать пользовательский конвертер, вы должны назвать ген RIC версия это так:

var deserialized = JsonConvert.DeserializeObject<PropertyInfo>(serialized, jsonSerializerSettings); 

То же самое верно в вашем примере, поэтому вам не поражали свою точку разрыва.

+0

Интересно, обернуть нарушителей, чтобы избежать прямой сериализации вместо использования пользовательского конвертера. Я предполагаю, что для обработки типичных типов это будет просто проверка типа в любом направлении и правильная обработка. –

+0

Если бы мне нужен конвертер, я бы использовал аналогичный код внутри конвертера. У вас возникнет проблема с использованием метода CustomCreationConverter, поскольку он был создан, потому что в 'Create'method у вас недостаточно информации для надежного создания экземпляра' PropertyInfo'. Он абстрактный, и вы, вероятно, сериализуете экземпляр «RuntimePropertyInfo», который не является общедоступным. Вместо этого я унаследовал бы от класса JsonConverter. Если вы хотите продолжить использование сериализации по умолчанию, переопределите свойство CanWrite и всегда возвращайте false. – Sacrilege

Смежные вопросы