簡體   English   中英

json.net:如何將“相似但不同”的外部json結構反序列化為單個類

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM