基于另一个属性值的属性上的自定义JsonConverter

罗德里戈·马丁斯

我有一个这样的物理尺寸类:

public class Physical
{
  public Dimension Dimension {get; set;}
  public double Value {get; set;}
  public string Unit {get; set;}
}

Dimension是与值喜欢,枚举ForceTemperatureDisplacementTime,等。

和具有Physical类似属性的类

public class MeasurementInfo
{
  public Instrument Instrument {get; set;}
  public Physical MaxReading {get; set;}
  public Physical MinReading {get; set;}
  public Physical AmbientTemperature {get; set;}
}

Instrument也与值喜欢的枚举ChronometerWeightScaleThermometerCaliper,等。

Dimension我的一些财产的价值Physical性依赖于Instrument价值。其他的是固定的。例:

var myMeasure = new MeasurementInfo()
{
  Instrument = Instrument.WeightScale,
  MaxReading = new Physical()
  {
    Dimension = Dimension.Weight,
    Value = 100.0,
    Unit = "kg"
  },
  MinReading = new Physical()
  {
    Dimension = Dimension.Weight,
    Value = 50.0,
    Unit = "kg"
  },
  AmbientTemperature = new Physical()
  {
    Dimension = Dimension.Temperature,
    Value = 27,
    Unit = "°C"
  }
};

我想要的是将此对象另存为JSON,如下所示:

{
  "Instrument": "Weight Scale",
  "Max Reading": "100 kg",
  "Min Reading": "50 kg",
  "AmbientTemperature": "27 °C"
}

序列化是容易的,因为我们有ValueUnit定义。我的问题是反序列化,因为我必须读取Instrument值以确定aDimension来重新创建Physical对象。

我的实际尝试是使用ContractResolver这样,我可以JsonConverter根据属性类型和名称来定义我的属性。

public class MyContractResolver : DefaultContractResolver
{
  protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
  {
    JsonProperty result = base.CreateProperty(member, memberSerialization);
    if(result.PropertyType == typeof(Physical))
    {
      var property = member as PropertyInfo;
      switch (result.PropertyName)
      {
        case "AmbientTemperature":
          result.Converter = new JsonPhysicalConverter(Dimension.Temperature);
          break;
        case "MaxReading":
        case "MinReading":
          result.Converter = new JsonPhysicalConverter(???);
          break;
      }
    }
  }
}

??? 是我卡住的地方。ContractResolver所以无法事先知道我并不为实例的工作Instrument价值。

布赖恩·罗杰斯

AJsonConverter无权访问其正在处理的对象的父级。因此,如果转换器进行处理Physical,它将无法“看到”Instrument内部MeasurementInfo

因此,我可以看到两种方法:

  1. MeasurementInfo除了之外,还使转换器处理父级Physical然后,转换器将能够看到InstrumentPhysical根据需要创建
  2. 使用单位值确定适当的Dimension例如,如果kg您知道单位Weight,则无论采用何种仪器,尺寸都必须为,对吗?仅当存在两个外观相同但代表不同尺寸的单元(取决于仪器)时,这种情况才会崩溃。但是根据您的示例,我认为情况并非如此。我认为这种方法将“更清洁”。

这是第一种方法的示例:

public class MeasurementInfoConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(MeasurementInfo);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject obj = JObject.Load(reader);
        MeasurementInfo info = new MeasurementInfo();
        info.Instrument = obj["Instrument"].ToObject<Instrument>(serializer);
        info.MinReading = ReadPhysical(obj, "Min Reading", info.Instrument);
        info.MaxReading = ReadPhysical(obj, "Max Reading", info.Instrument);
        info.AmbientTemperature = ReadPhysical(obj, "Ambient Temperature", Instrument.Thermometer);
        return info;
    }

    private Physical ReadPhysical(JObject obj, string name, Instrument instrument)
    {
        Dimension dim = Dimension.Force;
        switch (instrument)
        {
            case Instrument.WeightScale: dim = Dimension.Weight; break;
            case Instrument.Chronometer: dim = Dimension.Time; break;
            case Instrument.Thermometer: dim = Dimension.Temperature; break;
            case Instrument.Caliper:     dim = Dimension.Displacement; break;
        }
        string[] parts = ((string)obj[name]).Split(new char[] { ' ' }, 2);
        Physical physical = new Physical()
        {
            Dimension = dim,
            Value = double.Parse(parts[0]),
            Unit = parts[1]
        };
        return physical;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        MeasurementInfo info = (MeasurementInfo)value;
        JObject obj = new JObject();
        obj.Add("Instrument", JToken.FromObject(info.Instrument, serializer));
        WritePhysical(obj, "Min Reading", info.MinReading);
        WritePhysical(obj, "Max Reading", info.MaxReading);
        WritePhysical(obj, "Ambient Temperature", info.AmbientTemperature);
        obj.WriteTo(writer);
    }

    private void WritePhysical(JObject obj, string name, Physical physical)
    {
        obj.Add(name, physical.Value.ToString("N0") + " " + physical.Unit);
    }
}

工作往返演示:https//dotnetfiddle.net/ZUibQ1


为了完整起见,下面是第二种方法的示例:

public class PhysicalConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Physical);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        string value = (string)reader.Value;
        string[] parts = value.Split(new char[] { ' ' }, 2);
        Dimension dim;
        if (!DimensionsByUnit.TryGetValue(parts[1], out dim)) dim = Dimension.Force;
        Physical physical = new Physical()
        {
            Dimension = dim,
            Value = double.Parse(parts[0]),
            Unit = parts[1]
        };
        return physical;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Physical physical = (Physical)value;
        writer.WriteValue(physical.Value.ToString("N0") + " " + physical.Unit);
    }

    private static Dictionary<string, Dimension> DimensionsByUnit = new Dictionary<string, Dimension>
    {
        { "mg", Dimension.Weight },
        { "g", Dimension.Weight },
        { "kg", Dimension.Weight },
        { "°C", Dimension.Temperature },
        { "°F", Dimension.Temperature },
        { "°K", Dimension.Temperature },
        { "µs", Dimension.Time },
        { "ms", Dimension.Time },
        { "s", Dimension.Time },
        { "mm", Dimension.Displacement },
        { "cm", Dimension.Displacement },
        { "m", Dimension.Displacement },
    };
}

工作往返演示:https : //dotnetfiddle.net/1ecLNJ

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

CSS 自定义属性作为另一个属性的后备值而不是固定(预定义)值

根据另一个表中的自定义属性过滤值

如何从另一个指令更改指令自定义属性的值

从自定义控件的另一个属性获取在设计器中设置的属性值

将属性绑定到自定义控件的另一个属性

将动态创建的下拉列表的自定义属性值添加到另一个元素

基于另一个单元格值的自定义货币格式

关于另一个模型的属性的Rails自定义验证错误消息

WPF:自定义控件属性已被另一个自定义控件错误注册

基于另一个自己的属性定义一个类属性

如何遍历自定义类型列表并从另一个列表替换一个属性?

Xpages-自定义控件从另一个自定义控件获取自定义属性

基于另一个属性值的XPath用于属性值

基于另一个属性的值条件的属性值

打字稿:基于另一个属性的属性

如何通过自定义属性在另一个元素内的元素内获取文本

将prop作为参数传递给另一个prop自定义属性c#

根据另一个自定义数组对数组中的对象属性进行排序

基于另一个属性值的动态类型属性

使用json-schema要求或禁止基于另一个属性值的属性?

如何基于另一个属性的值访问嵌套的JSON属性?

Json模式。如何基于另一个属性值验证属性键?

在Java中创建Xpath以基于另一个属性值选择一个属性值

Watch无法在另一个自定义元素指令中使用的自定义属性指令中工作

如何在纯CSS中将定义的属性值用作另一个属性?

使用C#中的字典,将某些自定义类属性用作同一类中另一个属性(以值形式)的唯一键

使用另一个向量定义的自定义间隔对向量中的Bin值

根据另一个属性约束属性值

ChartJS:获取单个值减去自定义图例中的另一个值