Newtonsoft 使用交叉引用反序列化多个 JSON 文件
Newtonsoft deserialize multiple JSON files with cross references
我有两个 JSON 文件(我无法更改其格式),格式如下:
主文件 -
[
{
"Name":"XYZ",
"UnitReferenceId":1
},
{
"Name":"ABC",
"UnitReferenceId":2
}
]
lookup/referenceJSON文件-
[
{
"UnitReferenceId":1,
"Units":[
{
"Unit":"mg",
"Scale":1
},
{
"Unit":"gm",
"Scale":1000
},
{
"Unit":"kg",
"Scale":1000000
}
]
},
{
"UnitReferenceId":2,
"Units":[
{
"Unit":"mm",
"Scale":1
},
{
"Unit":"m",
"Scale":1000
},
{
"Unit":"km",
"Scale":1000000
}
]
}
]
我将如何使用 Newtonsoft JSON 将其反序列化为 C# 类 为:
public class Widget
{
public string Name {get; set;}
public UnitReference UnitReference { get; set; }
}
public class UnitReference
{
public long UnitReferenceId { get; set; }
public List<Unit> Units { get; set; }
}
public class Unit
{
[JsonProperty("Unit")]
public string UnitValue { get; set; }
public long Scale { get; set; }
}
如有任何帮助,我们将不胜感激!
您可以按如下方式阅读您的两个 JSON 文件来完成此操作:
首先读取 UnitReference
的 lookup/reference JSON 文件作为 List<UnitReference>
,然后转换为 Dictionary<long, UnitReference>
查找 table.
接下来,使用 Widget
的 custom JsonConverter
读取主文件,该文件已通过 Dictionary<long, UnitReference>
查找 table 并且可以在 UnitReferenceId
和 UnitReference
在读写期间。
因此您的 classes 将如下所示:
public class UnitReference
{
readonly long unitReferenceId;
public UnitReference(long unitReferenceId)
{
this.unitReferenceId = unitReferenceId;
}
public long UnitReferenceId { get { return unitReferenceId; } }
public List<Unit> Units { get; set; }
}
public class Unit
{
[JsonProperty("Unit")]
public string UnitValue { get; set; }
public long Scale { get; set; }
}
public class Widget
{
public string Name { get; set; }
public UnitReference UnitReference { get; set; }
}
(我唯一的修改是将 UnitReferenceId
设置为只读,以便它可以安全地用作字典键。)
然后,定义以下转换器:
public class WidgetConverter : CustomPropertyConverterBase<Widget>
{
readonly IDictionary<long, UnitReference> units;
public WidgetConverter(IDictionary<long, UnitReference> units)
{
this.units = units;
}
protected override void ReadCustomProperties(JObject obj, Widget value, JsonSerializer serializer)
{
var id = (long?)obj.GetValue("UnitReferenceId", StringComparison.OrdinalIgnoreCase);
if (id != null)
value.UnitReference = units[id.Value];
}
protected override bool ShouldSerialize(JsonProperty property, object value)
{
if (property.UnderlyingName == nameof(Widget.UnitReference))
return false;
return base.ShouldSerialize(property, value);
}
protected override void WriteCustomProperties(JsonWriter writer, Widget value, JsonSerializer serializer)
{
if (value.UnitReference != null)
{
writer.WritePropertyName("UnitReferenceId");
writer.WriteValue(value.UnitReference.UnitReferenceId);
}
}
}
public abstract class CustomPropertyConverterBase<T> : JsonConverter where T : class
{
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jObj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var value = existingValue as T ?? (T)contract.DefaultCreator();
ReadCustomProperties(jObj, value, serializer);
// Populate the remaining properties.
using (var subReader = jObj.CreateReader())
{
serializer.Populate(subReader, value);
}
return value;
}
protected abstract void ReadCustomProperties(JObject obj, T value, JsonSerializer serializer);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
writer.WriteStartObject();
foreach (var property in contract.Properties.Where(p => ShouldSerialize(p, value)))
{
var propertyValue = property.ValueProvider.GetValue(value);
if (propertyValue == null && serializer.NullValueHandling == NullValueHandling.Ignore)
continue;
writer.WritePropertyName(property.PropertyName);
serializer.Serialize(writer, propertyValue);
}
WriteCustomProperties(writer, (T)value, serializer);
writer.WriteEndObject();
}
protected virtual bool ShouldSerialize(JsonProperty property, object value)
{
return property.Readable && !property.Ignored && (property.ShouldSerialize == null || property.ShouldSerialize(value));
}
protected abstract void WriteCustomProperties(JsonWriter writer, T value, JsonSerializer serializer);
}
并反序列化如下:
var units = JsonConvert.DeserializeObject<List<UnitReference>>(unitsJsonString)
.ToDictionary(u => u.UnitReferenceId);
var settings = new JsonSerializerSettings
{
Converters = { new WidgetConverter(units) },
};
var widgets = JsonConvert.DeserializeObject<List<Widget>>(widgetsJsonString, settings);
备注:
出于演示目的,我在这里从 JSON 字符串反序列化,但您可以直接从文件反序列化,如 Deserialize JSON from a file.
所示
WidgetConverter
的基础 class CustomPropertyConverterBase<T>
自动读取和写入被(反)序列化对象的所有属性。 WidgetConverter
然后仅针对 UnitReference
属性 覆盖此行为,避免手动序列化 Widget
.
的所有剩余属性的必要性
示例 fiddle.
我用json2csharp for creating my classes quickly. If you have to implement it in code, see JSON C# Class Generator Project.
我有两个 JSON 文件(我无法更改其格式),格式如下:
主文件 -
[
{
"Name":"XYZ",
"UnitReferenceId":1
},
{
"Name":"ABC",
"UnitReferenceId":2
}
]
lookup/referenceJSON文件-
[
{
"UnitReferenceId":1,
"Units":[
{
"Unit":"mg",
"Scale":1
},
{
"Unit":"gm",
"Scale":1000
},
{
"Unit":"kg",
"Scale":1000000
}
]
},
{
"UnitReferenceId":2,
"Units":[
{
"Unit":"mm",
"Scale":1
},
{
"Unit":"m",
"Scale":1000
},
{
"Unit":"km",
"Scale":1000000
}
]
}
]
我将如何使用 Newtonsoft JSON 将其反序列化为 C# 类 为:
public class Widget
{
public string Name {get; set;}
public UnitReference UnitReference { get; set; }
}
public class UnitReference
{
public long UnitReferenceId { get; set; }
public List<Unit> Units { get; set; }
}
public class Unit
{
[JsonProperty("Unit")]
public string UnitValue { get; set; }
public long Scale { get; set; }
}
如有任何帮助,我们将不胜感激!
您可以按如下方式阅读您的两个 JSON 文件来完成此操作:
首先读取
UnitReference
的 lookup/reference JSON 文件作为List<UnitReference>
,然后转换为Dictionary<long, UnitReference>
查找 table.接下来,使用
Widget
的 customJsonConverter
读取主文件,该文件已通过Dictionary<long, UnitReference>
查找 table 并且可以在UnitReferenceId
和UnitReference
在读写期间。
因此您的 classes 将如下所示:
public class UnitReference
{
readonly long unitReferenceId;
public UnitReference(long unitReferenceId)
{
this.unitReferenceId = unitReferenceId;
}
public long UnitReferenceId { get { return unitReferenceId; } }
public List<Unit> Units { get; set; }
}
public class Unit
{
[JsonProperty("Unit")]
public string UnitValue { get; set; }
public long Scale { get; set; }
}
public class Widget
{
public string Name { get; set; }
public UnitReference UnitReference { get; set; }
}
(我唯一的修改是将 UnitReferenceId
设置为只读,以便它可以安全地用作字典键。)
然后,定义以下转换器:
public class WidgetConverter : CustomPropertyConverterBase<Widget>
{
readonly IDictionary<long, UnitReference> units;
public WidgetConverter(IDictionary<long, UnitReference> units)
{
this.units = units;
}
protected override void ReadCustomProperties(JObject obj, Widget value, JsonSerializer serializer)
{
var id = (long?)obj.GetValue("UnitReferenceId", StringComparison.OrdinalIgnoreCase);
if (id != null)
value.UnitReference = units[id.Value];
}
protected override bool ShouldSerialize(JsonProperty property, object value)
{
if (property.UnderlyingName == nameof(Widget.UnitReference))
return false;
return base.ShouldSerialize(property, value);
}
protected override void WriteCustomProperties(JsonWriter writer, Widget value, JsonSerializer serializer)
{
if (value.UnitReference != null)
{
writer.WritePropertyName("UnitReferenceId");
writer.WriteValue(value.UnitReference.UnitReferenceId);
}
}
}
public abstract class CustomPropertyConverterBase<T> : JsonConverter where T : class
{
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jObj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var value = existingValue as T ?? (T)contract.DefaultCreator();
ReadCustomProperties(jObj, value, serializer);
// Populate the remaining properties.
using (var subReader = jObj.CreateReader())
{
serializer.Populate(subReader, value);
}
return value;
}
protected abstract void ReadCustomProperties(JObject obj, T value, JsonSerializer serializer);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
writer.WriteStartObject();
foreach (var property in contract.Properties.Where(p => ShouldSerialize(p, value)))
{
var propertyValue = property.ValueProvider.GetValue(value);
if (propertyValue == null && serializer.NullValueHandling == NullValueHandling.Ignore)
continue;
writer.WritePropertyName(property.PropertyName);
serializer.Serialize(writer, propertyValue);
}
WriteCustomProperties(writer, (T)value, serializer);
writer.WriteEndObject();
}
protected virtual bool ShouldSerialize(JsonProperty property, object value)
{
return property.Readable && !property.Ignored && (property.ShouldSerialize == null || property.ShouldSerialize(value));
}
protected abstract void WriteCustomProperties(JsonWriter writer, T value, JsonSerializer serializer);
}
并反序列化如下:
var units = JsonConvert.DeserializeObject<List<UnitReference>>(unitsJsonString)
.ToDictionary(u => u.UnitReferenceId);
var settings = new JsonSerializerSettings
{
Converters = { new WidgetConverter(units) },
};
var widgets = JsonConvert.DeserializeObject<List<Widget>>(widgetsJsonString, settings);
备注:
出于演示目的,我在这里从 JSON 字符串反序列化,但您可以直接从文件反序列化,如 Deserialize JSON from a file.
所示WidgetConverter
的基础 classCustomPropertyConverterBase<T>
自动读取和写入被(反)序列化对象的所有属性。WidgetConverter
然后仅针对UnitReference
属性 覆盖此行为,避免手动序列化Widget
. 的所有剩余属性的必要性
示例 fiddle.
我用json2csharp for creating my classes quickly. If you have to implement it in code, see JSON C# Class Generator Project.