我有一个这样的物理维度类:
public class Physical
{
public Dimension Dimension {get; set;}
public double Value {get; set;}
public string Unit {get; set;}
}
Dimension
是一个具有Force
、Temperature
、Displacement
、Time
等值的枚举。
以及具有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
也是一个具有Chronometer
、WeightScale
、Thermometer
、Caliper
等值的枚举。
我的某些Physical
属性的Dimension
属性值取决于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"
}
序列化很容易,因为我们Value
和Unit
定义。我的问题是反序列化,因为我必须读取Instrument
值来确定重新创建Physical
对象的Dimension
。
我的实际尝试是使用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
值。
JsonConverter
无权访问它正在处理的对象的父级。 因此,如果转换器处理Physical
它将无法"看到"MeasurementInfo
内部的Instrument
。
所以我可以看到两种方法:
- 使转换器处理父
MeasurementInfo
以及Physical
。 然后,转换器将能够查看Instrument
并根据需要创建Physical
。 - 使用单位值确定适当的
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
使用匿名对象:
var objectToBeSerialized = new
{
Instrument = myMeasure.Instrument.ToString(),
MaxReading = $"{myMeasure.MaxReading.Value} {myMeasure.MaxReading.Unit}",
MinReading = $"{myMeasure.MinReading.Value} {myMeasure.MinReading.Unit}"
};
并使用NewtonSoft.Json库将此对象转换为JSON:
var serializedJSON = JsonConvert.SerializeObject(objectToBeSerialized);