Json.net上反序列化更改属性类型和名称

时间:2019-10-20 10:02:48

标签: c# json.net

使用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;
    }
}

1 个答案:

答案 0 :(得分:1)

您的基本问题是您的CustomContractResolver仅更改了返回的PropertyNamePropertyTypeJsonProperty,但是更改了从其创建的基础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