我试图将现有的JSON结构反序列化为由一组模型组成的对象。这些模型中的命名不一致,我被特别要求不要更改它们(重命名,添加属性等)。
所以,给定这个Json文本(只是一个小样本):{
"parameter": {
"alarms": [
{
"id": 1,
"name": "alarm1",
"type": 5,
"min": 0,
"max": 2
}],
"setting-active": true,
"setting-oneRun": true
}
}
需要映射到这些模型中:
public class Alarm
{
public int AlarmId { get; set; }
public string AlarmName { get; set; }
public AlarmType RbcType { get; set; }
public int MinimumTolerated { get; set; }
public int MaximumTolerated { get; set; }
}
public class Setting
{
public bool Active { get; set; }
public bool OneRun { get; set; }
}
public class Parameter
{
public List<Alarm> Alarms { get; set; }
public Setting ParameterSetting { get; set; }
}
到目前为止,我正在编写一个类来扩展DefaultContractResolver并覆盖映射的属性名。
MyCustomResolver so far:
public class MyCustomResolver : DefaultContractResolver
{
private Dictionary<string, string>? _propertyMappings;
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
//ModelMappings is a static class that will return a dictionary with mappings per ObjType being deserialized
_propertyMappings = ModelMappings.GetMapping(type);
return base.CreateProperties(type, memberSerialization);
}
protected override string ResolvePropertyName(string propertyName)
{
if (_propertyMappings != null)
{
_propertyMappings.TryGetValue(propertyName, out string? resolvedName);
return resolvedName ?? base.ResolvePropertyName(propertyName);
}
return base.ResolvePropertyName(propertyName);
}
}
我用来反序列化的代码:
var settings = new JsonSerializerSettings();
settings.DateFormatString = "YYYY-MM-DD";
settings.ContractResolver = new MyCustomResolver();
Parameter p = JsonConvert.DeserializeObject<Parameter>(jsonString, settings);
所以我到达了一个点,我需要以某种方式将Parameter中的属性映射到位于prev json节点中的值("setting-active", "setting-oneRun")。我需要告诉反序列化器这些值在哪里。这可以使用DefaultContractResolver的扩展来完成吗?
我很感激任何指向正确方向的建议
您可以在DefaultContractResolver.CreateObjectContract()
中应用ModelMappings.GetMapping(objectType)
:
public class MyCustomResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
var overrides = ModelMappings.GetMapping(objectType);
if (overrides != null)
{
foreach (var property in contract.Properties.Concat(contract.CreatorParameters))
{
if (property.UnderlyingName != null && overrides.TryGetValue(property.UnderlyingName, out var name))
property.PropertyName = name;
}
}
return contract;
}
}
指出:
通过应用
CreateObjectContract()
中的映射,您可以重新映射属性名称和创建器参数名称。由于契约解析器被设计为解析所有类型的契约,因此存储单个
private Dictionary<string, string>? _propertyMappings;
实际上没有意义。不像你之前的问题,你当前的问题显示了一个嵌套的c#对象
ParameterSetting
的属性被渗透到父对象Parameter
。由于自定义契约解析器被设计为为单个类型生成契约,因此它不适合在类型之间重构数据。相反,在这种情况下,可以考虑使用DTO或转换器+ DTO:public class ParameterConverter : JsonConverter<Parameter> { record ParameterDTO(List<Alarm> alarms, [property: JsonProperty("setting-active")] bool? Active, [property: JsonProperty("setting-oneRun")] bool? OneRun); public override void WriteJson(JsonWriter writer, Parameter? value, JsonSerializer serializer) { var dto = new ParameterDTO(value!.Alarms, value.ParameterSetting?.Active, value.ParameterSetting?.OneRun); serializer.Serialize(writer, dto); } public override Parameter? ReadJson(JsonReader reader, Type objectType, Parameter? existingValue, bool hasExistingValue, JsonSerializer serializer) { var dto = serializer.Deserialize<ParameterDTO>(reader); if (dto == null) return null; existingValue ??= new (); existingValue.Alarms = dto.alarms; if (dto.Active != null || dto.OneRun != null) existingValue.ParameterSetting = new () { Active = dto.Active.GetValueOrDefault(), OneRun = dto.OneRun.GetValueOrDefault() }; return existingValue; } }
如果你的"real"模型太复杂,无法定义DTO,您可以创建一个
JsonConverter<Paramater>
,它将JSON(反)序列化到中间的JToken
层次结构中,然后重新构建它。.在某些情况下,属性的自定义命名只是驼峰大小写。要在不需要显式覆盖的情况下驼峰大小写属性名,将
MyCustomResolver.NamingStrategy
设置为CamelCaseNamingStrategy
,例如:var settings = new JsonSerializerSettings { DateFormatString = "YYYY-MM-DD", // Use CamelCaseNamingStrategy since many properties in the JSON are just camel-cased. ContractResolver = new MyCustomResolver { NamingStrategy = new CamelCaseNamingStrategy() }, Converters = { new ParameterConverter() }, };
此处演示小提琴
我认为"保持简单"的最好方法是,您需要定义一个具有json属性的对象。然后你可以使用"Automapper"定义json对象之间的映射规则。以及"业务对象">