[英]json.net: how can I deserialize 'similar but different' external json structures to a single class
我正在尋找一種方法,最好使用JSON.NET(使用最新的方法)將多個外部JSON格式/結構反序列化為單個類。
簡化示例(差異大於此值,但不同的json包含類似信息):
外部1
{
"id": 1234,
"person": {
"name": "john",
"surname": "doe"
}
}
外部2
{
"ref": "1234",
"firstName": "JOHN",
"lastName": "DOE"
}
內部的(這不是真實的,僅用於顯示)
{
"tag": "1234",
"name1": "John",
"name2": "Doe"
}
是否有某種方式/庫可以允許您使用mapping.json文件配置映射。 最好是一個還允許格式化值等的格式。這僅是兩個示例,但我們還有更多示例。
編輯:我們可以告訴/提示JSON.NET,給定JSON來自哪個來源。 因此,我們不必具有可以處理所有不同場景的單一架構/合同/解決方案。 實際上,我們更願意為每個不同的外部json結構使用一個.json映射/轉換配置文件(無需重新構建所有內容即可將其保存為將來的證明)。
編輯2:基於Pavel Baravik的回答,我現在要做的是遍歷“ schema / transformation” JSON的所有屬性。 此JSON與要將原始外部JSON轉換為的對象的最終JSON具有相同的結構。 如果令牌的類型為“字符串”,我們將解析該字符串(支持{{}}和plain =),並將其用作從原始外部JSON提取值的路徑。 同時,正在構造最終的JSON,然后將其反序列化為我們的內部對象。
我認為我們可以通過使用“表達式”樹對代碼進行“分類”編譯來提高其性能。
static void Main(string[] args)
{
var schema = @"
{
""person"": {
""name"": ""=test.firstName"",
""fullName"": ""{{test.firstName}} {{lastName}}"",
""surName"": ""=lastName""
}
}";
var json1 = @"
{
""test"": {
""firstName"": ""John""
},
""lastName"": ""Doe"",
}";
Console.WriteLine(Transform(json1, schema).ToString());
Console.ReadLine();
}
public static JObject Transform(string json, string schema)
{
var j = JObject.Parse(json);
var s = JObject.Parse(schema);
var t = Transform(s, j);
return t;
}
public static JObject Transform(JObject schema, JObject source)
{
var target = new JObject();
foreach (var child in schema.Children())
{
var property = child as JProperty;
if (property != null)
{
var schemaToken = property.Value;
var allowClone = true;
JToken t = null;
if (schemaToken.Type == JTokenType.Object)
{
t = Transform((JObject) schemaToken, source);
}
else if (schemaToken.Type == JTokenType.String)
{
allowClone = false;
t = TransformProperty(source, (JValue)schemaToken);
}
if (t != null || allowClone)
{
target.Add(property.Name, (t ?? property).DeepClone());
}
}
}
return target;
}
private static readonly Regex MoustacheRegex = new Regex(@"\{\{[^\}]+\}\}", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Singleline);
private static JToken TransformProperty(JObject source, JValue jstring)
{
var str = (string)jstring.Value;
JToken t = null;
// if string starts with =
if (str.StartsWith("="))
{
t = GetTokenByPath(source, str.Substring(1));
}
else
{
var allFound = true;
str = MoustacheRegex.Replace(str, m =>
{
var mv = m.Value;
var mt = GetTokenByPath(source, mv.Substring(2, mv.Length - 4));
if (mt == null) allFound = false;
return mt?.ToString() ?? string.Empty;
});
if (allFound)
t = new JValue(str.Trim());
}
return t;
}
private static JToken GetTokenByPath(JObject source, string path)
{
JToken t = null;
var pathItems = path.Split('.');
var s = source;
for (var i = 0; i < pathItems.Length && s != null; ++i, s = t as JObject)
{
t = s[pathItems[i]];
}
return t;
}
編輯:(不錯的JTransform類)
public class JTransform
{
private InternalJTransform _internal;
public void Load(string filePath)
{
using (var stream = File.OpenRead(filePath))
using (var reader = new StreamReader(stream))
{
Load(new JsonTextReader(reader));
}
}
public void Load(string filePath, Encoding encoding)
{
using (var stream = File.OpenRead(filePath))
using (var reader = new StreamReader(stream, encoding))
{
Load(new JsonTextReader(reader));
}
}
public void Load(JsonReader reader)
{
_internal = new InternalJTransform(reader);
}
public JObject Transform(JsonReader sourceReader)
{
return _internal.Transform(sourceReader);
}
public JObject Transform(JObject source)
{
return _internal.Transform(source);
}
public T TransformObject<T>(object obj)
{
return _internal.TransformObject<T>(obj);
}
public T TransformObject<T>(JObject source, JsonSerializer serializer = null)
{
return _internal.TransformObject<T>(source, serializer);
}
#region InternalJTransform
private sealed class InternalJTransform
{
private static readonly Regex MoustacheRegex = new Regex(@"\{\{[^\}]+\}\}", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Singleline);
private JsonSerializer _serializer;
private JObject _template;
private bool _ignoreUndefined;
public InternalJTransform(JsonReader reader)
{
var json = JObject.Load(reader);
_template = json["template"] as JObject;
_serializer = new JsonSerializer();
var settings = json["settings"];
if (settings["camelCase"]?.Value<bool>() ?? false)
_serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
if (settings["ignoreNull"]?.Value<bool>() ?? false)
_serializer.NullValueHandling = NullValueHandling.Ignore;
_ignoreUndefined = (settings["ignoreUndefined"]?.Value<bool>() ?? settings["ignoreNull"]?.Value<bool>() ?? false);
}
private void Load(JsonReader reader)
{
var json = JObject.Load(reader);
var template = json["template"] as JObject;
var serializer = new JsonSerializer();
var settings = json["settings"];
if (settings["camelCase"]?.Value<bool>() ?? false)
serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
if (settings["ignoreNull"]?.Value<bool>() ?? false)
serializer.NullValueHandling = NullValueHandling.Ignore;
_ignoreUndefined = (settings["ignoreNull"]?.Value<bool>() ?? false);
_serializer = serializer;
_template = template;
}
public JObject Transform(JsonReader sourceReader)
{
var obj = JObject.Load(sourceReader);
return TransformInternal(_template, obj, _serializer);
}
public JObject Transform(JObject source)
{
return TransformInternal(_template, source, _serializer);
}
public T TransformObject<T>(object obj)
{
var source = JObject.FromObject(obj);
var im = TransformInternal(_template, source, _serializer);
return im.ToObject<T>(_serializer);
}
public T TransformObject<T>(JObject source, JsonSerializer serializer = null)
{
var obj = TransformInternal(_template, source, _serializer);
return obj.ToObject<T>(serializer ?? _serializer);
}
private JObject TransformInternal(JObject template, JObject source, JsonSerializer serializer)
{
var ignoreNull = serializer.NullValueHandling == NullValueHandling.Ignore;
var target = new JObject();
foreach (var property in template.Properties())
{
var token = property.Value;
if (token.Type == JTokenType.Object)
{
token = TransformInternal((JObject)token, source, serializer);
}
else if (token.Type == JTokenType.String)
{
token = TransformStringToken(source, (JValue)token);
// handle undefined, not found, values
if (token == null && _ignoreUndefined) continue;
}
// handle real null values (this does not include null values set in the template)
if (token != null && token.Type == JTokenType.Null && ignoreNull) continue;
target.Add(property.Name, token?.DeepClone());
}
return target;
}
private JToken TransformStringToken(JObject source, JValue jstring)
{
var str = (string)jstring.Value;
JToken t = null;
// if string starts with =
if (str.StartsWith("="))
{
t = GetTokenByPath(source, str.Substring(1));
}
else
{
var allFound = true;
str = MoustacheRegex.Replace(str, m =>
{
var mv = m.Value;
var mt = GetTokenByPath(source, mv.Substring(2, mv.Length - 4));
if (mt == null) allFound = false;
return mt?.ToString() ?? string.Empty;
});
if (allFound)
t = new JValue(str.Trim());
}
return t;
}
private static JToken GetTokenByPath(JObject source, string path)
{
JToken t = null;
var pathItems = path.Split('.');
var s = source;
for (var i = 0; i < pathItems.Length && s != null; ++i, s = t as JObject)
{
t = s[pathItems[i]];
}
return t;
}
}
#endregion
}
看看JSON.NET中的CustomCreationConverter http://www.newtonsoft.com/json/help/html/CustomCreationConverter.htm,您可以制作不同的轉換器,並根據您擁有的JSON決定使用哪個轉換器。 他們都可以輸出相同的類
您可以首先使用JsonReader“平展”您的輸入結構,然后映射到單個類(從JSON.NET中采用反序列化特定屬性的方式 )。
void Main()
{
var json0 = @"{
""id"": 1234,
""person"": {
""name"": ""john"",
""surname"": ""doe""
}";
var json1 = @" {
""ref"": ""1234"",
""firstName"": ""JOHN"",
""lastName"": ""DOE""
}";
foreach (var j in new []{json0, json1})
{
var name = GetFirstInstance<string>(new [] {"person.name", "firstName", "name1"}, j);
var surname = GetFirstInstance<string> (new [] {"person.surname", "lastName", "name2"}, j);
new {name, surname}.Dump();
}
}
public T GetFirstInstance<T>(string[] path, string json)
{
using (var stringReader = new StringReader(json))
using (var jsonReader = new JsonTextReader(stringReader))
{
while (jsonReader.Read())
{
if (jsonReader.TokenType == JsonToken.PropertyName && path.Contains((string)jsonReader.Path))
{
jsonReader.Read();
var serializer = new JsonSerializer();
return serializer.Deserialize<T>(jsonReader);
}
}
return default(T);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.