2017-04-12 147 views
1

我試圖序列一組包含了多種類型,包括到其他自定義類型的對象引用的對象。我希望在屬性成員都是默認值或null時排除這些對象引用。下面是設置:Newtonsoft.Json CustomContractResolver排除空對象節點

public class ObjectA 
{ 
    [DefaultValue(2)] 
    [JsonProperty(PropertyName = "propertyA")] 
    public int PropertyA { get; set; } = 6; 

    [JsonProperty(PropertyName = "objectB")] 
    public ObjectB ObjectB { get; set; } = new ObjectB(); 
} 

public class ObjectB 
{ 
    [DefaultValue(2)] 
    [JsonProperty(PropertyName = "propertyA")] 
    public int PropertyA { get; set; } = 2; 

    [JsonProperty(PropertyName = "propertyB")] 
    public string PropertyB { get; set; } 
} 

的問題是,當我序列化對象A使用下列內容:

var settings = new JsonSerializerSettings(); 

settings.NullValueHandling = NullValueHandling.Ignore; 
settings.DefaultValueHandling = DefaultValueHandling.Ignore; 

return JsonConvert.SerializeObject(ObjectA, settings); 

我想看到這一點:

{ 
    "propertyA": 6 
} 

但是我仍然看到的空對象屬性參考:

{ 
    "propertyA": 6, 
    "objectB" : {} 
} 

我想在JSON擺脫對象B的,只有把它顯示出來,如果它的成員之一,是不是缺省值或空。雖然這個例子只顯示了一層嵌套,但它需要適用於任何級別的對象嵌套。

+0

你確定你不希望看到這一點 - '{ 「propertyA」:6, 「對象B」:{「propertyA 「:2}}','因爲太ObjectB'有沒有空屬性正如'PropertyA'在'ObjectA' ... – gkb

+0

的DefaultValueHandling.Ignore應在對象B從結果中排除PropertyA。 – Jamie

+0

它不應該忽略objectA中的PropertyA嗎? – gkb

回答

0

所以我制定了一個醜陋的解決方案,它可以遞歸地減少空的Json節點,同時保持默認實例化的嵌套對象。該解決方案包括使用與某些類型的映射,以減少將JSON沿着使用遞歸一個DefaultContractResolver實現。

這裏是合同解析:

public class ShouldSerializeContractResolver : DefaultContractResolver 
{ 
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 
    { 
     JsonProperty property = base.CreateProperty(member, memberSerialization); 

     if (property.GetType().GetTypeName() == "object") 
     { 
      property.ShouldSerialize = 
       instance => 
       { 
        var value = instance.GetType().GetProperty(property.UnderlyingName).GetValue(instance, null); 

        if (value == null) 
        { 
         return false; 
        } 

        if (value.GetType().GetTypeName() == "object") 
        { 
         if (NodeHasValue(value)) 
         { 
          return true; 
         } 
        } 
        else 
        { 
         if (value.GetType().GetTypeName() == "collection") 
         { 
          ICollection enumerable = (ICollection)value; 
          if (enumerable.Count != 0) 
          { 
           return true; 
          } 
          else 
          { 
           return false; 
          } 
         } 

         return true; 
        } 
        return false; 

       }; 
     } 

     return property; 
    } 

    public bool NodeHasValue(object obj) 
    { 
     Type objType = obj.GetType(); 
     PropertyInfo[] properties = objType.GetProperties(); 

     foreach (PropertyInfo property in properties) 
     { 
      var value = property.GetValue(obj, null); 

      if (value == null) 
      { 
       return false; 
      } 

      if (value.GetType().GetTypeName() == "object") 
      { 
       return NodeHasValue(value); 
      } 

      if (value.GetType().GetTypeName() == "collection") 
      { 
       ICollection enumerable = (ICollection)value; 
       if (enumerable.Count != 0) 
       { 
        return true; 
       } 
      } 

      if (value.GetType().GetTypeName() == "array") 
      { 
       IList enumerable = (IList)value; 
       if (enumerable.Count != 0) 
       { 
        return true; 
       } 
      } 

      if (value.GetType().GetTypeName() != "object" 
       && value.GetType().GetTypeName() != "collection" 
       && value.GetType().GetTypeName() != "array") 
      { 
       if (value != null) 
       { 
        var attribute = property.GetCustomAttribute(typeof(DefaultValueAttribute)) as DefaultValueAttribute; 

        if (attribute.Value.ToString() != value.ToString()) 
        { 
         return true; 
        } 
       } 
      } 
     } 

     return false; 
    } 
} 

的方法GetTypeName()是該類型類的擴展方法,它的工作原理是什麼我指定的身份是原始類型與集合,對象和數組。對於GetTypeName

擴展方法類():

public static string GetTypeName(this Type type) 
{ 
    if (type.IsArray) 
    { 
     return "array"; 
    } 

    if (type.GetTypeInfo().IsGenericType) 
    { 
     if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) 
     { 
      return GetTypeName(Nullable.GetUnderlyingType(type)) + '?'; 
     } 

     var genericTypeDefName = type.Name.Substring(0, type.Name.IndexOf('`')); 
     var genericTypeArguments = string.Join(", ", type.GenericTypeArguments.Select(GetTypeName)); 

     if (type.GetTypeInfo().GetInterfaces().Any(ti => ti.GetGenericTypeDefinition() == typeof(IEnumerable<>))) 
     { 
      return "collection"; 
     } 

     return $"{genericTypeDefName}<{genericTypeArguments}>"; 
    } 

    string typeName; 
    if (_primitiveTypeNames.TryGetValue(type, out typeName)) 
    { 
     return typeName; 
    } 

    // enum's come up as a ValueType so we check IsEnum first. 
    if (type.GetTypeInfo().IsEnum) 
    { 
     return "enum"; 
    } 

    if (type.GetTypeInfo().IsValueType) 
    { 
     return "struct"; 
    } 

    return "object"; 
} 

private static readonly Dictionary<Type, string> _primitiveTypeNames = new Dictionary<Type, string> 
{ 
    { typeof(bool), "bool" }, 
    { typeof(byte), "byte" }, 
    { typeof(byte[]), "byte[]" }, 
    { typeof(sbyte), "sbyte" }, 
    { typeof(short), "short" }, 
    { typeof(ushort), "ushort" }, 
    { typeof(int), "int" }, 
    { typeof(uint), "uint" }, 
    { typeof(long), "long" }, 
    { typeof(ulong), "ulong" }, 
    { typeof(char), "char" }, 
    { typeof(float), "float" }, 
    { typeof(double), "double" }, 
    { typeof(string), "string" }, 
    { typeof(decimal), "decimal" } 
}; 

}

0

的問題是與ObjectB本身的默認值作爲一個對象,而不是與當你序列化ObjectA它具有屬性的默認值。

如果你學習的範例here,他們都提到了nullable類型和預期違約,對於object S的是null

此選項會忽略所有的默認值(例如空對象和可空類型; 0表示整數,小數點和浮點數;布爾值爲false)。

爲了說明這意味着什麼,嘗試序列化ObjectB與默認 -

var objectB = new ObjectB 
{ 
    PropertyA = 2 //The default value is also 2. 
}; 


string serialized = JsonConvert.SerializeObject(objectB, 
          Newtonsoft.Json.Formatting.Indented, 
          new JsonSerializerSettings {         
           DefaultValueHandling = DefaultValueHandling.Ignore 
          }); 

而你得到的是{}

現在,如果你明確設置ObjectBnull,只有串行會忽略它作爲整體的object -

var objectA = new ObjectA 
{ 
    PropertyA = 6, 
    ObjectB = null 
}; 
string serialized = JsonConvert.SerializeObject(objectA, 
          Newtonsoft.Json.Formatting.Indented, 
          new JsonSerializerSettings {         
           DefaultValueHandling = DefaultValueHandling.Ignore 
          }); 

,你會得到預期的結果 -

{ 
    "propertyA": 6 
} 

你可以用不同的值嘗試,看看是否符合您的期望。

+0

你的例子是正確的,但是我需要從一開始就實例化的對象。我將在這裏使用的場景是將用於配置應用程序的選項/設置類。如果他們不需要重新設置他們想要配置的每個設置區域,開發人員的體驗會更好。 – Jamie