我正在尝试序列化一组包含各种类型的对象,包括对其他自定义类型的对象引用。我希望在它的属性成员都是默认值或null时排除这些对象引用。这是设置:
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时:
var settings = new JsonSerializerSettings();
settings.NullValueHandling = NullValueHandling.Ignore;
settings.DefaultValueHandling = DefaultValueHandling.Ignore;
return JsonConvert.SerializeObject(ObjectA, settings);
我想看到这个:
{
"propertyA": 6
}
但是我仍然看到一个空的对象属性引用:
{
"propertyA": 6,
"objectB" : {}
}
我想摆脱json中的objectB,只有当其中一个成员不是默认值或null时才显示它。虽然此示例仅显示一个嵌套级别,但对于任何级别的对象嵌套都需要保持为真。
答案 0 :(得分:0)
问题在于ObjectB
本身的默认值是一个对象,而不是序列化ObjectA
时它具有的属性的默认值。
如果您浏览示例here,他们就提到了nullable
类型的预期默认值以及object
的{{1}}
此选项将忽略所有默认值(例如,对象和可空类型为null;对于整数,小数和浮点数为0;对于布尔值为false)。
为说明含义,请尝试使用默认值<{1}}序列化
null
你得到的是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
});
设置为{}
,那么序列化程序只会将其忽略为ObjectB
-
null
您将得到预期的结果 -
object
您可以尝试使用不同的值,看看这是否符合您的期望。
答案 1 :(得分:0)
所以我已经找到了一个丑陋的解决方案,它可以递归地减少空的Json节点,同时维护默认情况下实例化的嵌套对象。该解决方案涉及使用DefaultContractResolver实现,该实现使用递归和某些类型映射来减少Json。
这是合同解析员:
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()是Type类的扩展方法,它用于标识我指定的基本类型与集合,对象和数组。
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" }
};
}