反序列化JsonValue以在System.Json中建模

问题描述

在任何人开始建议诸如Newtonsoft.JsonSystem.Text.Json之类的任何库或我想使用的其他任何好而简单的东西之前,要知道我无法使用System.Json以外的任何东西,因为我在一个应用程序的约束下工作,(我在制作插件),所以我没有影响力并且已经停止了积极的开发,(它是一个ERP系统,每年仅进行一次安全补丁,并且功能请求导致被动攻击性的回应;即使我主动提出要免费进行更改,也是如此。

我有一些json和一些不错的领域模型(对象,类,模型,实体,无论您想用属性调用公共类),我都希望他们结婚。而且当嵌套时使用反射是一种痛苦。

有人可以告诉我一些不需要任何nugets或dll的好方法吗?我搜索时发现的所有内容都与System.Json以外的所有其他库有关。

这是我放弃之前一直在做的事情,(我重构为看起来像用例的东西,但这是由于合同原因):

public void BuildSettings(string settingsPath = "appsettings.json",params Type[] types)
{
    if (!types.Any())
        throw new ArgumentException("The type parameters cannot be empty",nameof(types));

    var file = new FileInfo(settingsPath);
    if (!file.Exists)
        throw new ArgumentException($"No settings file found in the path '{settingsPath}'",nameof(settingsPath));

    using (var reader = file.OpenText())
    {
        var rootJson = JsonValue.Load(reader);

        if (rootJson.JsonType != JsonType.Object)
            throw new ArgumentException($"The settings file must be a Json Object,but a '{rootJson.JsonType}' was found",nameof(settingsPath));

        var jsonObject = rootJson as JsonObject;

        if (jsonObject == null)
            throw new NullReferenceException("The json object is null");

        foreach (var type in types)
        {
            if (jsonObject.ContainsKey(type.Name))
            {
                var jsonSetting = jsonObject[type.Name] as JsonObject;
                var properties = type.GetProperties();

                foreach (var property in properties)
                {
                    var value = jsonSetting[property.Name];
                    var propertyType = property.PropertyType;
                    property.SetValue();
                    // Todo: Ask StackOwerflow
                }
            }
        }
    }
}

这有些愚蠢,但我没有制定规则

解决方法

我想您已经待了很长时间了……在我看来,您必须开发一个ORM,一次我不得不为ATM机制作ORM时也遇到了同样的情况。

此代码将假定IDataReader,因此对您不起作用,但是您需要使用反射进行属性映射,并且该代码已在其中。

让我知道这样做是否成功,因为它具有一些优化功能,可以重用反射类型等。

我认为您需要测试作为类的属性,并使用激活器创建它,并使用有效的强制转换

if (property.PropertyType.BaseType == typeof(Enum))
{
    property.SetValue(obj,(int)value);
}
else if (property.PropertyType.BaseType == typeof(Guid))
{
   property.SetValue(obj,Guid.Parse(value.ToString().ToUpper()));
}
else
{
  property.SetValue(obj,Convert.ChangeType(value,property.PropertyType));
}

我不知道您需要如何支持我的电话,或者如果它是固定金额,您可能只是在创建静态T Parse(此T目标,字符串json)方法之后 在其中使用{和}括号对json字符串进行分段以获取属性,并在[和]处获取数组。

这是代码,我将它用于我前段时间制作的VCARD Json解析器

/// <summary>
/// Splits the specified string in sections of open en closing characters.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="open">The opening char indicating where to start to read .</param>
/// <param name="close">The close char,indicating the part where should stop reading.</param>
/// <returns>IReadOnlyList&lt;System.String&gt;.</returns>
/// <exception cref="System.ArgumentNullException">text</exception>
/// <exception cref="ArgumentNullException">Will throw an exception if the string that needs to be split is null or empty</exception>
public static IReadOnlyList<string> Split(this string text,char open,char close)
{
    if (text is null)
    {
        throw new ArgumentNullException(nameof(text));
    }

    var counted = 0;
    var result = new List<string>();
    var sb = new StringBuilder();
    foreach (char c in text)
    {
        if (c == open)
        {
            if (counted != 0)
                sb.Append(c);

            counted++;
            continue;
        }
        if (c == close)
        {
            counted--;
            if (counted != 0)
                sb.Append(c);
            continue;
        }

        if (counted > 0)
        {
            sb.Append(c);
        }
        else if (counted == 0 && sb.Length > 0)
        {
            result.Add(sb.ToString());
            sb.Clear();
        }
    }
    return result;
}

这是我必须制作的完整映射器,您必须在其中看到提到的反射

class Mapper
{
    ConcurrentDictionary<Type,PropertyInfo[]> _properties = new ConcurrentDictionary<Type,PropertyInfo[]>();
    ConcurrentDictionary<string,List<string>> _fieldNames = new ConcurrentDictionary<string,List<string>>();

    /// <summary>
    /// Maps the specified reader to a given class. the reader must contain all properties of the type provided.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="reader">The reader.</param>
    /// <returns></returns>
    public IEnumerable<T> Map<T>(SqlDataReader reader)
    {
        var result = new List<T>();
        if (!reader.HasRows)
            return result;


        var type = typeof(T);
        if (!_properties.TryGetValue(type,out PropertyInfo[] prop))
        {
            prop = type.GetProperties();
            _properties.TryAdd(type,prop);
        }

  
        if (!_fieldNames.TryGetValue(type.Name,out List<string> fieldNames))
        {
            var names = new List<string>(reader.FieldCount);
            for (int i = 0; i < reader.FieldCount; i++)
            {
                names.Add(reader.GetName(i));
            }
            fieldNames = names;
            _fieldNames.TryAdd(type.Name,fieldNames);
        }

        while (reader.Read())
        {
            var obj = Activator.CreateInstance<T>();
            foreach (var property in prop)
            {
                if (fieldNames.Contains(property.Name))
                {
                    var value = reader[property.Name];
                    if (value == DBNull.Value)
                        continue;

                    if (property.PropertyType.BaseType == typeof(Enum))
                    {
                        property.SetValue(obj,(int)value);
                    }
                    else if (property.PropertyType.BaseType == typeof(Guid))
                    {
                        property.SetValue(obj,Guid.Parse(value.ToString().ToUpper()));
                    }
                    else
                    {
                        property.SetValue(obj,property.PropertyType));
                    }

                }
            }
            result.Add(obj);
        }
        return result;
    }

    public IEnumerable<T> Map<T,Y>(SqlDataReader reader,Y owner)
    {
        var result = new List<T>();
        if (!reader.HasRows)
            return result;


        var type = typeof(T);
        if (!_properties.TryGetValue(type,prop);
        }

        if (!_fieldNames.TryGetValue(type.Name,fieldNames);
        }

        while (reader.Read())
        {
            var obj = Activator.CreateInstance<T>();
            foreach (var property in prop)
            {

                if (property.PropertyType == typeof(Y))
                {
                    property.SetValue(obj,owner);
                    continue;
                }

                if (fieldNames.Contains(property.Name))
                {
                    var value = reader[property.Name];
                    if (value == DBNull.Value)
                        continue;

                    if (property.PropertyType.BaseType == typeof(Enum))
                    {
                        property.SetValue(obj,(int)value);
                    }
                    else
                    {
                        property.SetValue(obj,property.PropertyType));
                    }

                }
            }
            result.Add(obj);
        }
        return result;
    }

    /// <summary>
    /// Maps the specified reader to a given class. the reader must contain all properties of the type provided.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="reader">The reader.</param>
    /// <returns></returns>
    public T MapOne<T>(SqlDataReader reader)
    {

        if (!reader.HasRows)
            return default;

            
        var type = typeof(T);
        if (!_properties.TryGetValue(type,prop);
        }


        if (!_fieldNames.TryGetValue(type.Name,out  List<string> fieldNames))
        {
            var names = new List<string>(reader.FieldCount);
            for (int i = 0; i < reader.FieldCount; i++)
            {
                names.Add(reader.GetName(i));
            }
            fieldNames = names;
            _fieldNames.TryAdd(type.Name,fieldNames);
        }

        if (reader.Read())
        {
            var obj = Activator.CreateInstance<T>();
            foreach (var property in prop)
            {
                if (fieldNames.Contains(property.Name))
                    property.SetValue(obj,reader[property.Name]);
            }
            return obj;
        }
        else
        {
            return default;
        }

    }


    /// <summary>
    /// Maps the specified reader to a given class. the reader must contain all properties of the type provided.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="reader">The reader.</param>
    /// <returns></returns>
    public IEnumerable<T> Map<T>(SqlDataReader reader,object[] args)
    {
        var result = new List<T>();
        if (!reader.HasRows)
            return result;

 
        var type = typeof(T);
        if (!_properties.TryGetValue(type,fieldNames);
        }

        while (reader.Read())
        {
            var obj = (T)Activator.CreateInstance(type,args);
            foreach (var property in prop)
            {
                if (fieldNames.Contains(property.Name))
                    property.SetValue(obj,reader[property.Name]);
            }
            result.Add(obj);
        }
        return result;
    }
}