2015-10-20 2 views
0

Я ищу способ частичной сериализации модели для Json, используя Json.Net. Как должна выглядеть частичная сериализация, которую я хочу определить в свойстве родительского объекта. Таким образом, частичная сериализация может отличаться для разных исходных моделей. Для того, чтобы проиллюстрировать, что я хочу здесь код:Частичная сериализация модели на основе атрибута свойства

private class MyTestObject 
{ 
    [SerializeOnly("TestValue1")] 
    [SerializeOnly("TestValue3")] 
    public ComplexTestObject Property1 { get; set; } 

    public MyTestObject() 
    { 
     Property1 = new ComplexTestObject(); 
    } 
} 

private class ComplexTestObject 
{ 
    public string TestValue1 { get; set; } 
    public string TestValue2 { get; set; } 
    public string TestValue3 { get; set; } 
    public ComplexTestObject() 
    { 
     TestValue1 = "value1"; 
     TestValue2 = "value2"; 
     TestValue3 = "value3"; 
    } 
} 

Теперь, когда я сериализовать экземпляр класса MyTestObject Я хочу, чтобы получить следующий JSon:

{ 
    "Property1" : { 
     "TestValue1" : "value1", 
     "TestValue3" : "value3", 
    } 
} 

Вы можете увидеть, что SerializeOnly используется для определения того, какие свойства должны быть сериализованы.

Для этого я могу создать SerializeOnlyAttribute. При попытке использовать это в пользовательском Serialization ContractResolver я могу видеть только атрибуты конкретного элемента, и поэтому я не вижу никаких SerializeOnlyAttribute, потому что они находятся на родительском.

Есть ли простой способ добиться желаемого поведения с помощью Json.Net? Возможно, можно написать собственный JsonConverter, но как он может быть построен таким образом, чтобы покрывать только обработку атрибутов, и все еще используются преобразователи по умолчанию?

ответ

1

Вы можете решить эту проблему на две части:

  • Создать пользовательский JsonConverter, который может принимать список имена свойств для сериализации.
  • Создайте пользовательский ContractResolver, который ищет свойства, у которых есть хотя бы один атрибут [SerializeOnly], и применяйте настраиваемый конвертер к этим свойствам, передавая список имен дочерних свойств, полученных из прикладных атрибутов.

Вот что распознаватель может выглядеть следующим образом:

class CustomResolver : DefaultContractResolver 
{ 
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) 
    { 
     IList<JsonProperty> props = base.CreateProperties(type, memberSerialization); 
     foreach (JsonProperty prop in props) 
     { 
      if (!prop.PropertyType.IsPrimitive && prop.PropertyType != typeof(string)) 
      { 
       PropertyInfo pi = type.GetProperty(prop.UnderlyingName); 
       if (pi != null && pi.CanRead) 
       { 
        var childPropertiesToSerialize = pi.GetCustomAttributes<SerializeOnly>() 
                 .Select(att => att.PropertyName); 
        if (childPropertiesToSerialize.Any()) 
        { 
         prop.Converter = new CustomConverter(childPropertiesToSerialize); 
        } 
       } 
      } 
     } 
     return props; 
    } 
} 

А вот преобразователь:

class CustomConverter : JsonConverter 
{ 
    private HashSet<string> propertiesToSerialize; 

    public CustomConverter(IEnumerable<string> propertiesToSerialize) 
    { 
     this.propertiesToSerialize = new HashSet<string>(propertiesToSerialize); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     writer.WriteStartObject(); 
     foreach (PropertyInfo prop in value.GetType().GetProperties()) 
     { 
      if (prop.CanRead && propertiesToSerialize.Contains(prop.Name)) 
      { 
       writer.WritePropertyName(prop.Name); 
       serializer.Serialize(writer, prop.GetValue(value)); 
      } 
     } 
     writer.WriteEndObject(); 
    } 

    public override bool CanRead 
    { 
     get { return false; } 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 

    public override bool CanConvert(Type objectType) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Демо:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var test = new MyTestObject(); 
     var settings = new JsonSerializerSettings(); 
     settings.ContractResolver = new CustomResolver(); 
     settings.Formatting = Formatting.Indented; 
     var json = JsonConvert.SerializeObject(test, settings); 
     Console.WriteLine(json); 
    } 

    class MyTestObject 
    { 
     [SerializeOnly("TestValue1")] 
     [SerializeOnly("TestValue3")] 
     public ComplexTestObject Property1 { get; set; } 

     [SerializeOnly("TestValue2")] 
     public ComplexTestObject Property2 { get; set; } 

     public MyTestObject() 
     { 
      Property1 = new ComplexTestObject(); 
      Property2 = new ComplexTestObject(); 
     } 
    } 

    class ComplexTestObject 
    { 
     public string TestValue1 { get; set; } 
     public string TestValue2 { get; set; } 
     public string TestValue3 { get; set; } 
     public ComplexTestObject() 
     { 
      TestValue1 = "value1"; 
      TestValue2 = "value2"; 
      TestValue3 = "value3"; 
     } 
    } 
} 

Выход:

{ 
    "Property1": { 
    "TestValue1": "value1", 
    "TestValue3": "value3" 
    }, 
    "Property2": { 
    "TestValue2": "value2" 
    } 
} 

Fiddle: https://dotnetfiddle.net/Fj7QcW

+0

Удивительный! Мне нравится это решение! Thx :-) – Sjoerd222888

+0

Нет проблем; Рад, что смог помочь. –

-1

C# уже имеет атрибут, чтобы помочь вам с этим

https://msdn.microsoft.com/en-us/library/system.web.script.serialization.scriptignoreattribute(v=vs.110).aspx

[ScriptIgnore] 
public string MyParam{get;set;} 
+0

Сначала в Json.NET это было бы [JsonIgnore]. А во-вторых, полное свойство не сериализуется, но я хочу частично его сериализовать. И я хочу определить поведение родителя! – Sjoerd222888

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