反序列化json -映射没有直接json匹配的单个属性



我试图将现有的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对象之间的映射规则。以及"业务对象">

相关内容

  • 没有找到相关文章

最新更新