Jam*_*mie 6 c# parsing json json.net
我正在尝试序列化一组包含各种类型的对象,包括对其他自定义类型的对象引用。我希望当属性成员均为默认值或 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; }
}
Run Code Online (Sandbox Code Playgroud)
问题是当我使用以下命令序列化 ObjectA 时:
var settings = new JsonSerializerSettings();
settings.NullValueHandling = NullValueHandling.Ignore;
settings.DefaultValueHandling = DefaultValueHandling.Ignore;
return JsonConvert.SerializeObject(ObjectA, settings);
Run Code Online (Sandbox Code Playgroud)
我想看看这个:
{
"propertyA": 6
}
Run Code Online (Sandbox Code Playgroud)
但是我仍然看到一个空的对象属性引用:
{
"propertyA": 6,
"objectB" : {}
}
Run Code Online (Sandbox Code Playgroud)
我想删除 json 中的 objectB,并且仅当它的成员之一不是默认值或 null 时才显示它。虽然此示例仅显示一层嵌套,但它需要适用于任何级别的对象嵌套。
所以我制定了一个丑陋的解决方案,它可以递归地减少空的 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;
}
}
Run Code Online (Sandbox Code Playgroud)
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" }
};
Run Code Online (Sandbox Code Playgroud)
}