2017-04-12 1 views
1

다른 사용자 지정 형식에 대한 개체 참조를 포함하여 다양한 형식을 포함하는 개체 집합을 serialize하려고합니다. 속성 멤버가 모두 기본값이거나 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; } 
} 

문제는 그 내가 사용 ObjectA를 직렬화 할 때 다음과 같은 : 나는 아직도보고하고 그러나

{ 
    "propertyA": 6 
} 

:

var settings = new JsonSerializerSettings(); 

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

return JsonConvert.SerializeObject(ObjectA, settings); 

내가이보고 싶어 빈 객체 속성 참조 :

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

json에서 objectB를 없애고 멤버 중 하나가 기본값 또는 null이 아닌 경우에만 표시하려고합니다. 이 예제는 하나의 중첩 레벨 만 표시하지만 모든 오브젝트 중첩 레벨에 대해 true를 유지해야합니다.

+0

당신은 당신이이보고 싶지 않아인가 ": 2}}', 왜냐하면'ObjectB'도'ObjectA'의'PropertyA'와 같이 null이 아닌 프로퍼티를 가지고 있기 때문입니다 ... – gkb

+0

DefaultValueHandling.Ignore는 ObjectB의 PropertyA를 결과에서 제외해야합니다. – Jamie

+0

objectA의 PropertyA도 무시하면 안됩니까? – gkb

답변

0

그래서 기본적으로 인스턴스화 된 중첩 된 개체를 유지하면서 비어있는 Json 노드를 줄이기 위해 작동하는 추한 솔루션을 만들었습니다. 이 솔루션에는 재귀를 사용하여 Json을 줄이기 위해 일부 유형 매핑과 함께 DefaultContractResolver 구현을 사용하는 것이 포함됩니다.여기

가 계약 확인자입니다 : 내가 지정한 어떤 정체성 작동, Type 클래스에 대한 확장 방법

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()되어 컬렉션, 객체, 배열 대 기본 유형입니다. 6, "objectB": { "propertyA - : GetTypeName에 대한

확장 방법 클래스()`{"propertyA "

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을 serialize 할 때 해당 속성의 기본값을 사용하지 않는 것이 문제입니다. 당신은 예를 here 통과하면

, 그들은 예상 nullable 유형에 대한 기본값 및 null

입니다 object들에 대한 것을 언급 한 모든 기본 값을 무시합니다이 옵션 (예 : 널 오브젝트와 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 
          }); 

을 그리고 당신이 얻을 것은 {}입니다. 명시 적으로이 nullObjectB을 설정 경우 지금

만 다음 시리얼 라이저는 전체에 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