.Net Core 3.0 JsonSerializer заполняет существующий объект - PullRequest
27 голосов
/ 01 июля 2019

Я готовлюсь к переходу с ASP.NET Core 2.2 на 3.0.

Поскольку я не использую более продвинутые функции JSON (но, может быть, одну, как описано ниже), а 3.0 теперь поставляется свстроенное пространство имен / классы для JSON, System.Text.Json, я решил посмотреть, смогу ли я удалить предыдущее значение по умолчанию Newtonsoft.Json.Заметьте, я знаю, что System.Text.Json не полностью заменит Newtonsoft.Json.

Мне удалось сделать это везде, например,

var obj = JsonSerializer.Parse<T>(jsonstring);

var jsonstring = JsonSerializer.ToString(obj);

, но в одном месте, где я населяюсуществующий объект.

С помощью Newtonsoft.Json можно сделать

JsonConvert.PopulateObject(jsonstring, obj);

Встроенное пространство имен System.Text.Json имеет несколько дополнительных классов, таких как JsonDocumnet, JsonElementи Utf8JsonReader, хотя я не могу найти ни одного, который бы принимал существующий объект в качестве параметра.

Также у меня недостаточно опыта, чтобы понять, как использовать существующий объект.

Там может быть возможной новой возможностью в .Net Core (спасибо Mustafa Gursel за ссылку), но между тем (и что, если это не так), ...

... Теперь я задаюсь вопросом, возможно ли достичь чего-то подобного, как то, что можно сделать с PopulateObject?

Я имею в виду, возможно ли с любым другим System.Text.Json классом выполнитьто же самое, и обновить / заменить только набор свойств?, ... или какой-нибудь другой умный обходной путь?


Вот пример того, как этоможет выглядеть так (и он должен быть универсальным, поскольку объект, передаваемый в метод десериализации, имеет тип <T>).У меня есть 2 строки Json, которые нужно проанализировать в объекте, где в первом установлены некоторые свойства по умолчанию, а во втором - например,

Обратите внимание, что значение свойства может быть любого другого типа, кроме string.

Строка Json 1:

{
  "Title": "Startpage",
  "Link": "/index",
}

Строка Json 2:

{
  "Head": "Latest news"
  "Link": "/news"
}

Используя приведенные выше 2 строки Json, я хочу получить объект, полученный в результатеin:

{
  "Title": "Startpage",
  "Head": "Latest news",
  "Link": "/news"
}

Как видно из вышеприведенного примера, если свойства в 2nd имеют значения / установлены, он заменяет значения в 1st (как в случае с "Head" и "Link"), если нет, то существуетзначение сохраняется (как в случае с «Заголовком»)

Ответы [ 7 ]

8 голосов
/ 05 июля 2019

Итак, если предположить, что Core 3 не поддерживает это из коробки, давайте попробуем обойти это.Итак, в чем наша проблема?

Нам нужен метод, который перезаписывает некоторые свойства существующего объекта таковыми из строки json.Таким образом, наш метод будет иметь подпись:

void PopulateObject<T>(T target, string jsonSource) where T : class

Нам не нужен какой-либо пользовательский синтаксический анализ, поскольку он громоздок, поэтому мы попробуем очевидный подход - десериализовать jsonSource и скопировать свойства результата внаш объект.Мы не можем, однако, просто пойти

T updateObject = JsonSerializer.Parse<T>(jsonSource);
CopyUpdatedProperties(target, updateObject);

Это потому, что для типа

class Example
{
    int Id { get; set; }
    int Value { get; set; }
}

и JSON

{
    "Id": 42
}

мы получим updateObject.Value == 0.Теперь мы не знаем, является ли 0 новым обновленным значением или оно просто не было обновлено, поэтому нам нужно точно знать, какие свойства jsonSource содержит.

К счастью, System.Text.Json APIпозволяет нам исследовать структуру проанализированного JSON.

var json = JsonDocument.Parse(jsonSource).RootElement;

Теперь мы можем перечислить все свойства и скопировать их.

foreach (var property in json.EnumerateObject())
{
    OverwriteProperty(target, property);
}

Мы скопируем значение, используя отражение:

void OverwriteProperty<T>(T target, JsonProperty updatedProperty) where T : class
{
    var propertyInfo = typeof(T).GetProperty(updatedProperty.Name);

    if (propertyInfo == null)
    {
        return;
    }

    var propertyType = propertyInfo.PropertyType;
    var parsedValue = JsonSerializer.Parse(updatedProperty.Value, propertyType);

    propertyInfo.SetValue(target, parsedValue);
} 

Здесь мы видим, что мы делаем обновление shallow .Если объект содержит в качестве своего свойства другой сложный объект, он будет скопирован и перезаписан целиком, а не обновлен.Если вам требуются обновления deep , этот метод необходимо изменить, чтобы извлечь текущее значение свойства, а затем рекурсивно вызвать PopulateObject, если тип свойства является ссылочным типом (который также потребует принятия * 1036).* как параметр в PopulateObject).

Объединив все это, мы получим:

void PopulateObject<T>(T target, string jsonSource) where T : class
{
    var json = JsonDocument.Parse(jsonSource).RootElement;

    foreach (var property in json.EnumerateObject())
    {
        OverwriteProperty(target, property);
    }
}

void OverwriteProperty<T>(T target, JsonProperty updatedProperty) where T : class
{
    var propertyInfo = typeof(T).GetProperty(updatedProperty.Name);

    if (propertyInfo == null)
    {
        return;
    }

    var propertyType = propertyInfo.PropertyType;
    var parsedValue = JsonSerializer.Parse(updatedProperty.Value, propertyType);

    propertyInfo.SetValue(target, parsedValue);
} 

Насколько это надежно?Что ж, это определенно не будет делать ничего разумного для массива JSON, но я не уверен, как можно ожидать, что метод PopulateObject будет работать с массивом для начала.Я не знаю, как она сравнивается по производительности с версией Json.Net, вам придется проверить это самостоятельно.Он также автоматически игнорирует свойства, которые не относятся к целевому типу.Я думал, что это был самый разумный подход, но вы могли бы подумать иначе, в этом случае свойство null-check должно быть заменено на исключение.

EDIT:

Я пошел дальше и реализовал глубокую копию:

void PopulateObject<T>(T target, string jsonSource) where T : class => 
    PopulateObject(target, jsonSource, typeof(T));

void OverwriteProperty<T>(T target, JsonProperty updatedProperty) where T : class =>
    OverwriteProperty(target, updatedProperty, typeof(T));

void PopulateObject(object target, string jsonSource, Type type)
{
    var json = JsonDocument.Parse(jsonSource).RootElement;

    foreach (var property in json.EnumerateObject())
    {
        OverwriteProperty(target, property, type);
    }
}

void OverwriteProperty(object target, JsonProperty updatedProperty, Type type)
{
    var propertyInfo = type.GetProperty(updatedProperty.Name);

    if (propertyInfo == null)
    {
        return;
    }

    var propertyType = propertyInfo.PropertyType;
    object parsedValue;

    if (propertyType.IsValueType)
    {
        parsedValue = JsonSerializer.Parse(updatedProperty.Value, propertyType);
    }
    else
    {
        parsedValue = propertyInfo.GetValue(target);
        PopulateObject(parsedValue, updatedProperty.Value, propertyType);
    }

    propertyInfo.SetValue(target, parsedValue);
}

Чтобы сделать это более надежным, вам потребуется либо отдельный метод PopulateObjectDeep, либо пропуск PopulateObjectOptions, либо что-то похожее с глубоким /мелкий флаг.

РЕДАКТИРОВАТЬ 2:

Точка глубокого копирования такова, что если у нас есть объект

{
    "Id": 42,
    "Child":
    {
        "Id": 43,
        "Value": 32
    },
    "Value": 128
}

и заполнить егос

{
    "Child":
    {
        "Value": 64
    }
}

мы получили бы

{
    "Id": 42,
    "Child":
    {
        "Id": 43,
        "Value": 64
    },
    "Value": 128
}

В случае мелкой копии мы получили бы Id = 0 в скопированном дочернем элементе.

6 голосов
/ 07 июля 2019

Обходной путь также может быть таким простым (также поддерживается многоуровневый JSON):

using System;
using System.Linq;
using System.Reflection;
using System.Text.Json.Serialization;

namespace ConsoleApp
{
    public class Model
    {
        public Model()
        {
            SubModel = new SubModel();
        }

        public string Title { get; set; }
        public string Head { get; set; }
        public string Link { get; set; }
        public SubModel SubModel { get; set; }
    }

    public class SubModel
    {
        public string Name { get; set; }
        public string Description { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var model = new Model();

            Console.WriteLine(JsonSerializer.ToString(model));

            var json1 = "{ \"Title\": \"Startpage\", \"Link\": \"/index\" }";

            model = Map<Model>(model, json1);

            Console.WriteLine(JsonSerializer.ToString(model));

            var json2 = "{ \"Head\": \"Latest news\", \"Link\": \"/news\", \"SubModel\": { \"Name\": \"Reyan Chougle\" } }";

            model = Map<Model>(model, json2);

            Console.WriteLine(JsonSerializer.ToString(model));

            var json3 = "{ \"Head\": \"Latest news\", \"Link\": \"/news\", \"SubModel\": { \"Description\": \"I am a Software Engineer\" } }";

            model = Map<Model>(model, json3);

            Console.WriteLine(JsonSerializer.ToString(model));

            var json4 = "{ \"Head\": \"Latest news\", \"Link\": \"/news\", \"SubModel\": { \"Description\": \"I am a Software Programmer\" } }";

            model = Map<Model>(model, json4);

            Console.WriteLine(JsonSerializer.ToString(model));

            Console.ReadKey();
        }

        public static T Map<T>(T obj, string jsonString) where T : class
        {
            var newObj = JsonSerializer.Parse<T>(jsonString);

            foreach (var property in newObj.GetType().GetProperties())
            {
                if (obj.GetType().GetProperties().Any(x => x.Name == property.Name && property.GetValue(newObj) != null))
                {
                    if (property.GetType().IsClass && property.PropertyType.Assembly.FullName == typeof(T).Assembly.FullName)
                    {
                        MethodInfo mapMethod = typeof(Program).GetMethod("Map");
                        MethodInfo genericMethod = mapMethod.MakeGenericMethod(property.GetValue(newObj).GetType());
                        var obj2 = genericMethod.Invoke(null, new object[] { property.GetValue(newObj), JsonSerializer.ToString(property.GetValue(newObj)) });

                        foreach (var property2 in obj2.GetType().GetProperties())
                        {
                            if (property2.GetValue(obj2) != null)
                            {
                                property.GetValue(obj).GetType().GetProperty(property2.Name).SetValue(property.GetValue(obj), property2.GetValue(obj2));
                            }
                        }
                    }
                    else
                    {
                        property.SetValue(obj, property.GetValue(newObj));
                    }
                }
            }

            return obj;
        }
    }
}

Выход:

enter image description here

5 голосов
/ 06 июля 2019

Вот пример кода, который это делает.Он использует новую Utf8JsonReader struct , поэтому он заполняет объект одновременно с его анализом.Он поддерживает эквивалентность типов JSON / CLR, вложенные объекты (создаются, если они не существуют), списки и массивы.

var populator = new JsonPopulator();
var obj = new MyClass();
populator.PopulateObject(obj, "{\"Title\":\"Startpage\",\"Link\":\"/index\"}");
populator.PopulateObject(obj, "{\"Head\":\"Latest news\",\"Link\":\"/news\"}");

public class MyClass
{
    public string Title { get; set; }
    public string Head { get; set; }
    public string Link { get; set; }
}

Обратите внимание, что он не поддерживает все то, что вы, вероятно, ожидаете, но вы можетепереопределить или настроить его.Что можно добавить: 1) соглашение об именах.Вам придется переопределить метод GetProperty.2) словари или расширенные объекты.3) производительность может быть улучшена, потому что она использует Reflection вместо методов MemberAccessor / делегата

public class JsonPopulator
{
    public void PopulateObject(object obj, string jsonString, JsonSerializerOptions options = null) => PopulateObject(obj, jsonString != null ? Encoding.UTF8.GetBytes(jsonString) : null, options);
    public virtual void PopulateObject(object obj, ReadOnlySpan<byte> jsonData, JsonSerializerOptions options = null)
    {
        options ??= new JsonSerializerOptions();
        var state = new JsonReaderState(new JsonReaderOptions { AllowTrailingCommas = options.AllowTrailingCommas, CommentHandling = options.ReadCommentHandling, MaxDepth = options.MaxDepth });
        var reader = new Utf8JsonReader(jsonData, isFinalBlock: true, state);
        new Worker(this, reader, obj, options);
    }

    protected virtual PropertyInfo GetProperty(ref Utf8JsonReader reader, JsonSerializerOptions options, object obj, string propertyName)
    {
        if (obj == null)
            throw new ArgumentNullException(nameof(obj));

        if (propertyName == null)
            throw new ArgumentNullException(nameof(propertyName));

        var prop = obj.GetType().GetProperty(propertyName);
        return prop;
    }

    protected virtual bool SetPropertyValue(ref Utf8JsonReader reader, JsonSerializerOptions options, object obj, string propertyName)
    {
        if (obj == null)
            throw new ArgumentNullException(nameof(obj));

        if (propertyName == null)
            throw new ArgumentNullException(nameof(propertyName));

        var prop = GetProperty(ref reader, options, obj, propertyName);
        if (prop == null)
            return false;

        if (!TryReadPropertyValue(ref reader, options, prop.PropertyType, out var value))
            return false;

        prop.SetValue(obj, value);
        return true;
    }

    protected virtual bool TryReadPropertyValue(ref Utf8JsonReader reader, JsonSerializerOptions options, Type propertyType, out object value)
    {
        if (propertyType == null)
            throw new ArgumentNullException(nameof(reader));

        if (reader.TokenType == JsonTokenType.Null)
        {
            value = null;
            return !propertyType.IsValueType || Nullable.GetUnderlyingType(propertyType) != null;
        }

        if (propertyType == typeof(object)) { value = ReadValue(ref reader); return true; }
        if (propertyType == typeof(string)) { value = JsonSerializer.ReadValue<JsonElement>(ref reader, options).GetRawText(); return true; }
        if (propertyType == typeof(int) && reader.TryGetInt32(out var i32)) { value = i32; return true; }
        if (propertyType == typeof(long) && reader.TryGetInt64(out var i64)) { value = i64; return true; }
        if (propertyType == typeof(DateTime) && reader.TryGetDateTime(out var dt)) { value = dt; return true; }
        if (propertyType == typeof(DateTimeOffset) && reader.TryGetDateTimeOffset(out var dto)) { value = dto; return true; }
        if (propertyType == typeof(Guid) && reader.TryGetGuid(out var guid)) { value = guid; return true; }
        if (propertyType == typeof(decimal) && reader.TryGetDecimal(out var dec)) { value = dec; return true; }
        if (propertyType == typeof(double) && reader.TryGetDouble(out var dbl)) { value = dbl; return true; }
        if (propertyType == typeof(float) && reader.TryGetSingle(out var sgl)) { value = sgl; return true; }
        if (propertyType == typeof(uint) && reader.TryGetUInt32(out var ui32)) { value = ui32; return true; }
        if (propertyType == typeof(ulong) && reader.TryGetUInt64(out var ui64)) { value = ui64; return true; }
        if (propertyType == typeof(byte[]) && reader.TryGetBytesFromBase64(out var bytes)) { value = bytes; return true; }

        if (propertyType == typeof(bool))
        {
            if (reader.TokenType == JsonTokenType.False || reader.TokenType == JsonTokenType.True)
            {
                value = reader.GetBoolean();
                return true;
            }
        }

        // fallback here
        return TryConvertValue(ref reader, propertyType, out value);
    }

    protected virtual object ReadValue(ref Utf8JsonReader reader)
    {
        switch (reader.TokenType)
        {
            case JsonTokenType.False: return false;
            case JsonTokenType.True: return true;
            case JsonTokenType.Null: return null;
            case JsonTokenType.String: return reader.GetString();

            case JsonTokenType.Number: // is there a better way?
                if (reader.TryGetInt32(out var i32))
                    return i32;

                if (reader.TryGetInt64(out var i64))
                    return i64;

                if (reader.TryGetUInt64(out var ui64)) // uint is already handled by i64
                    return ui64;

                if (reader.TryGetSingle(out var sgl))
                    return sgl;

                if (reader.TryGetDouble(out var dbl))
                    return dbl;

                if (reader.TryGetDecimal(out var dec))
                    return dec;

                break;
        }
        throw new NotSupportedException();
    }

    // we're here when json types & property types don't match exactly
    protected virtual bool TryConvertValue(ref Utf8JsonReader reader, Type propertyType, out object value)
    {
        if (propertyType == null)
            throw new ArgumentNullException(nameof(reader));

        if (propertyType == typeof(bool))
        {
            if (reader.TryGetInt64(out var i64)) // one size fits all
            {
                value = i64 != 0;
                return true;
            }
        }

        // TODO: add other conversions

        value = null;
        return false;
    }

    protected virtual object CreateInstance(ref Utf8JsonReader reader, Type propertyType)
    {
        if (propertyType.GetConstructor(Type.EmptyTypes) == null)
            return null;

        // TODO: handle custom instance creation
        try
        {
            return Activator.CreateInstance(propertyType);
        }
        catch
        {
            // swallow
            return null;
        }
    }

    private class Worker
    {
        private readonly Stack<WorkerProperty> _properties = new Stack<WorkerProperty>();
        private readonly Stack<object> _objects = new Stack<object>();

        public Worker(JsonPopulator populator, Utf8JsonReader reader, object obj, JsonSerializerOptions options)
        {
            _objects.Push(obj);
            WorkerProperty prop;
            WorkerProperty peek;
            while (reader.Read())
            {
                switch (reader.TokenType)
                {
                    case JsonTokenType.PropertyName:
                        prop = new WorkerProperty();
                        prop.PropertyName = Encoding.UTF8.GetString(reader.ValueSpan);
                        _properties.Push(prop);
                        break;

                    case JsonTokenType.StartObject:
                    case JsonTokenType.StartArray:
                        if (_properties.Count > 0)
                        {
                            object child = null;
                            var parent = _objects.Peek();
                            PropertyInfo pi = null;
                            if (parent != null)
                            {
                                pi = populator.GetProperty(ref reader, options, parent, _properties.Peek().PropertyName);
                                if (pi != null)
                                {
                                    child = pi.GetValue(parent); // mimic ObjectCreationHandling.Auto
                                    if (child == null && pi.CanWrite)
                                    {
                                        if (reader.TokenType == JsonTokenType.StartArray)
                                        {
                                            if (!typeof(IList).IsAssignableFrom(pi.PropertyType))
                                                break;  // don't create if we can't handle it
                                        }

                                        if (reader.TokenType == JsonTokenType.StartArray && pi.PropertyType.IsArray)
                                        {
                                            child = Activator.CreateInstance(typeof(List<>).MakeGenericType(pi.PropertyType.GetElementType())); // we can't add to arrays...
                                        }
                                        else
                                        {
                                            child = populator.CreateInstance(ref reader, pi.PropertyType);
                                            if (child != null)
                                            {
                                                pi.SetValue(parent, child);
                                            }
                                        }
                                    }
                                }
                            }

                            if (reader.TokenType == JsonTokenType.StartObject)
                            {
                                _objects.Push(child);
                            }
                            else if (child != null) // StartArray
                            {
                                peek = _properties.Peek();
                                peek.IsArray = pi.PropertyType.IsArray;
                                peek.List = (IList)child;
                                peek.ListPropertyType = GetListElementType(child.GetType());
                                peek.ArrayPropertyInfo = pi;
                            }
                        }
                        break;

                    case JsonTokenType.EndObject:
                        _objects.Pop();
                        if (_properties.Count > 0)
                        {
                            _properties.Pop();
                        }
                        break;

                    case JsonTokenType.EndArray:
                        if (_properties.Count > 0)
                        {
                            prop = _properties.Pop();
                            if (prop.IsArray)
                            {
                                var array = Array.CreateInstance(GetListElementType(prop.ArrayPropertyInfo.PropertyType), prop.List.Count); // array is finished, convert list into a real array
                                prop.List.CopyTo(array, 0);
                                prop.ArrayPropertyInfo.SetValue(_objects.Peek(), array);
                            }
                        }
                        break;

                    case JsonTokenType.False:
                    case JsonTokenType.Null:
                    case JsonTokenType.Number:
                    case JsonTokenType.String:
                    case JsonTokenType.True:
                        peek = _properties.Peek();
                        if (peek.List != null)
                        {
                            if (populator.TryReadPropertyValue(ref reader, options, peek.ListPropertyType, out var item))
                            {
                                peek.List.Add(item);
                            }
                            break;
                        }

                        prop = _properties.Pop();
                        var current = _objects.Peek();
                        if (current != null)
                        {
                            populator.SetPropertyValue(ref reader, options, current, prop.PropertyName);
                        }
                        break;
                }
            }
        }

        private static Type GetListElementType(Type type)
        {
            if (type.IsArray)
                return type.GetElementType();

            foreach (Type iface in type.GetInterfaces())
            {
                if (!iface.IsGenericType) continue;
                if (iface.GetGenericTypeDefinition() == typeof(IDictionary<,>)) return iface.GetGenericArguments()[1];
                if (iface.GetGenericTypeDefinition() == typeof(IList<>)) return iface.GetGenericArguments()[0];
                if (iface.GetGenericTypeDefinition() == typeof(ICollection<>)) return iface.GetGenericArguments()[0];
                if (iface.GetGenericTypeDefinition() == typeof(IEnumerable<>)) return iface.GetGenericArguments()[0];
            }
            return typeof(object);
        }
    }

    private class WorkerProperty
    {
        public string PropertyName;
        public IList List;
        public Type ListPropertyType;
        public bool IsArray;
        public PropertyInfo ArrayPropertyInfo;

        public override string ToString() => PropertyName;
    }
}
3 голосов
/ 05 июля 2019

Я не знаю много об этой новой версии плагина, однако я нашел учебник, которому можно следовать учебник с некоторыми примерами

Основываясь на нем, я подумал об этом методе и представляю, что он способен решить свою проблему

//To populate an existing variable we will do so, we will create a variable with the pre existing data
object PrevData = YourVariableData;

//After this we will map the json received
var NewObj = JsonSerializer.Parse<T>(jsonstring);

CopyValues(NewObj, PrevData)

//I found a function that does what you need, you can use it
//source: /4197132/obedinenie-dvuh-obektov-v-c
public void CopyValues<T>(T target, T source)
{

    if (target == null) throw new ArgumentNullException(nameof(target));
    if (source== null) throw new ArgumentNullException(nameof(source));

    Type t = typeof(T);

    var properties = t.GetProperties(
          BindingFlags.Instance | BindingFlags.Public).Where(prop => 
              prop.CanRead 
           && prop.CanWrite 
           && prop.GetIndexParameters().Length == 0);

    foreach (var prop in properties)
    {
        var value = prop.GetValue(source, null);
        prop.SetValue(target, value, null);
    }
}
1 голос
/ 05 июля 2019

Если это только одно использование, и вы не хотите добавлять дополнительные зависимости / много кода, вы не против немного неэффективности и Я не пропустил что-то очевидное, вы можете простоиспользование:

    private static T ParseWithTemplate<T>(T template, string input) 
    {
        var ignoreNulls = new JsonSerializerOptions() { IgnoreNullValues = true };
        var templateJson = JsonSerializer.ToString(template, ignoreNulls);
        var combinedData = templateJson.TrimEnd('}') + "," + input.TrimStart().TrimStart('{');
        return JsonSerializer.Parse<T>(combinedData);
    }
1 голос
/ 05 июля 2019

Я не уверен, что это решит вашу проблему, но это должно работать как временное решение. Все, что я сделал, это написал простой класс с методом populateobject.

public class MyDeserializer
{
    public static string PopulateObject(string[] jsonStrings)
    {
        Dictionary<string, object> fullEntity = new Dictionary<string, object>();

        if (jsonStrings != null && jsonStrings.Length > 0)
        {
            for (int i = 0; i < jsonStrings.Length; i++)
            {

                var myEntity = JsonSerializer.Parse<Dictionary<string, object>>(jsonStrings[i]);

                foreach (var key in myEntity.Keys)
                {
                    if (!fullEntity.ContainsKey(key))
                    {
                        fullEntity.Add(key, myEntity[key]);
                    }
                    else
                    {
                        fullEntity[key] = myEntity[key];
                    }
                }
            }
        }

        return JsonSerializer.ToString(fullEntity);
    }    
}

Я положил его в консольное приложение для тестирования. Ниже приведено все приложение, если вы хотите проверить его самостоятельно.

using System;
using System.Text.Json;
using System.IO;
using System.Text.Json.Serialization;

namespace JsonQuestion1
{
    class Program
    {
        static void Main(string[] args)
        {
            // Only used for testing
            string path = @"C:\Users\Path\To\JsonFiles";
            string st1 = File.ReadAllText(path + @"\st1.json");
            string st2 = File.ReadAllText(path + @"\st2.json");
            // Only used for testing ^^^

            string myObject = MyDeserializer.PopulateObject(new[] { st1, st2 } );

            Console.WriteLine(myObject);
            Console.ReadLine();

        }
    }

    public class MyDeserializer
    {
    public static string PopulateObject(string[] jsonStrings)
    {
        Dictionary<string, object> fullEntity = new Dictionary<string, object>();

        if (jsonStrings != null && jsonStrings.Length > 0)
        {
            for (int i = 0; i < jsonStrings.Length; i++)
            {

                var myEntity = JsonSerializer.Parse<Dictionary<string, object>>(jsonStrings[i]);

                foreach (var key in myEntity.Keys)
                {
                    if (!fullEntity.ContainsKey(key))
                    {
                        fullEntity.Add(key, myEntity[key]);
                    }
                    else
                    {
                        fullEntity[key] = myEntity[key];
                    }
                }
            }
        }

            return JsonSerializer.ToString(fullEntity);
      }
    }
}

Содержимое файла Json:

st1.json

{
    "Title": "Startpage",
    "Link": "/index"
}

st2.json

{
  "Title": "Startpage",
  "Head": "Latest news",
  "Link": "/news"
}
1 голос
/ 05 июля 2019

Если вы уже используете AutoMapper в своем проекте или не возражаете против зависимости от него, вы можете объединить объекты следующим образом:

var configuration = new MapperConfiguration(cfg => cfg
    .CreateMap<Model, Model>()
    .ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != default)));
var mapper = configuration.CreateMapper();

var destination = new Model {Title = "Startpage", Link = "/index"};
var source = new Model {Head = "Latest news", Link = "/news"};

mapper.Map(source, destination);

class Model
{
    public string Head { get; set; }
    public string Title { get; set; }
    public string Link { get; set; }
}
...