Ваша основная проблема заключается в том, что ваши CustomContractResolver
только изменяют PropertyName
и PropertyType
возвращенных JsonProperty
, однако основнойPropertyInfo
, из которого он был создан, все еще принадлежит государственной суррогатной собственности, а не частной внутренней "реальной" собственности. Таким образом, ValueProvider
, среди прочего, все равно будет неправильным.
Вместо этого вам нужно сгенерировать JsonProperty
для внутреннего свойства, исправить его имя и доступностьи вернуть его вместо JsonProperty
для государственной собственности. Это гарантирует, что сериализатор будет сериализовать и десериализовать внутреннее свойство вместо его общедоступного суррогата.
Следующий преобразователь контракта выполняет работу:
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();
и десериализации следующим образом:
var settings = new JsonSerializerSettings
{
ContractResolver = customContractResolver,
};
var root = JsonConvert.DeserializeObject<Nbgv>(json, settings);
Примечания:
Вна ваш вопрос вы указываете Мне нужно сопоставить список json с другим полем , однако в фактическом примере базовым элементом является свойство . Таким образом, в CreateProperty()
я проверяю оба типа замены. Если в вашем производственном коде вам нужен только один или другой, вы можете удалить ненужную логику.
Проверка !jProperty.HasMemberAttribute
предотвращает свойства, явно помеченные [JsonProperty]
от замены. Это кажется правильным, но вы можете снять чек, если не хотите.
Демонстрационная скрипка здесь .