使用json.net反序列化json字符串时,我需要将json列表映射到另一个字段并转换类型。
为清楚起见,我有一个此类(它是生成的代码,我无法更改):
[Serializable]
public partial class Nbgv
{
public virtual IReadOnlyList<string> BuildMetadataWithCommitId => BuildMetadataWithCommitIdInternal.AsReadOnly();
internal List<string> BuildMetadataWithCommitIdInternal { get; set; } = new List<string>();
}
我有这个json字符串:
string json = @"{
""BuildMetadataWithCommitId"": [
""c390a213b7""
]
}";
您可以看到BuildMetadataWithCommitId
取决于BuildMetadataWithCommitIdInternal
。
我使用自定义解析器尝试了几件事,但没有任何效果。我没有发现如何告诉json.net,如果您找到的属性是通用列表,则将其映射到以* Internal结尾的字段。
private class CustomContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.Writable = true;
if (IsList(property.PropertyType))
{
property.PropertyName += "Internal";
property.PropertyType = ToList(property.PropertyType);
}
return property;
}
private Type ToList(Type t)
{
var genericListType = typeof(List<>);
var specificListType = genericListType.MakeGenericType(t.GenericTypeArguments[0]);
return specificListType;
}
private bool IsList(Type t)
{
if (t == null) return false;
var res = t.IsGenericType &&
t.GetGenericTypeDefinition().IsAssignableFrom(typeof(IReadOnlyList<>));
return res;
}
}
答案 0 :(得分:1)
您的基本问题是您的CustomContractResolver
仅更改了返回的PropertyName
的PropertyType
和JsonProperty
,但是更改了从其创建的基础PropertyInfo
仍然是公共代理财产的财产,而不是私有内部“不动产”财产。因此,ValueProvider
仍然是错误的。
您需要做的是为内部属性生成一个JsonProperty
,更正其名称和可访问性,然后将其返回代替 公共财产。这将确保序列化程序将对内部属性进行序列化和反序列化,而不是对其公共代理进行替代。
以下合同解析器可以完成这项工作:
JsonProperty
要使用它,请将解析器的实例缓存到performance:
public class CustomContractResolver : DefaultContractResolver
{
const string InternalSuffix = "Internal";
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = member as PropertyInfo;
var jProperty = base.CreateProperty(member, memberSerialization);
if (property != null && jProperty != null && memberSerialization != MemberSerialization.Fields && !jProperty.HasMemberAttribute)
{
var replacementName = jProperty.UnderlyingName + InternalSuffix;
// Check for replacement property.
var replacementProperty = jProperty.DeclaringType.GetProperty(replacementName, BindingFlags.Instance | BindingFlags.NonPublic);
if (replacementProperty != null
&& replacementProperty.GetGetMethod(true) != null && replacementProperty.GetSetMethod(true) != null
&& ReplacementTypeCompatible(property, replacementProperty.PropertyType)
)
{
var replacementJProperty = base.CreateProperty(replacementProperty, memberSerialization);
replacementJProperty.PropertyName = jProperty.PropertyName;
if (!replacementJProperty.Readable && replacementProperty.GetGetMethod(true) != null)
replacementJProperty.Readable = true;
if (!replacementJProperty.Writable && replacementProperty.GetSetMethod(true) != null)
replacementJProperty.Writable = true;
return replacementJProperty;
}
// Check for replacement field.
var replacementField = jProperty.DeclaringType.GetField(replacementName, BindingFlags.Instance | BindingFlags.NonPublic);
if (replacementField != null
&& ReplacementTypeCompatible(property, replacementField.FieldType)
)
{
var replacementJProperty = base.CreateProperty(replacementField, memberSerialization);
replacementJProperty.PropertyName = jProperty.PropertyName;
replacementJProperty.Readable = true;
replacementJProperty.Writable = true;
return replacementJProperty;
}
}
return jProperty;
}
static bool ReplacementTypeCompatible(PropertyInfo property, Type replacementType)
{
// Add here whatever restrictions you need
if (property.PropertyType.IsGenericType && typeof(IReadOnlyList<>).IsAssignableFrom(property.PropertyType.GetGenericTypeDefinition())
&& replacementType.IsGenericType && typeof(List<>).IsAssignableFrom(replacementType.GetGenericTypeDefinition())
&& replacementType.GetGenericArguments().SequenceEqual(property.PropertyType.GetGenericArguments()))
return true;
return false;
}
}
反序列化如下:
static IContractResolver customContractResolver = new CustomContractResolver();
注意:
在您的问题中,您声明我需要将json列表映射到另一个字段 ,但是在实际示例中,基础成员是属性。因此,在CreateProperty()
中,我检查两种替换类型。如果在生产代码中只需要一个,则可以删除不需要的逻辑。
检查var settings = new JsonSerializerSettings
{
ContractResolver = customContractResolver,
};
var root = JsonConvert.DeserializeObject<Nbgv>(json, settings);
可以防止替换显式标记为[JsonProperty]
的属性。这似乎是正确的,但是如果您不想要,可以将其删除。
演示小提琴here。