【问题标题】:Deserialize JsonValue to model in System.Json反序列化 JsonValue 以在 System.Json 中建模
【发布时间】:2020-10-31 05:40:36
【问题描述】:

在任何人开始推荐任何库之前,例如 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
                }
            }
        }
    }
}

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

【问题讨论】:

    标签: c# json .net json-deserialization system.json


    【解决方案1】:

    我猜你已经待了很长时间......在我看来你必须开发一个 ORM,我曾经面临过同样的情况,我必须为 ATM 机制作一个 ORM。

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

    让我知道这是否对你有用,因为它对重用反射类型等进行了一些优化。

    我认为您需要测试一个属性是否为 Class 并使用激活器创建它并使用有效的强制转换使用

    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(this T target, string 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, Convert.ChangeType(value, 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, 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 (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, Convert.ChangeType(value, 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, 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);
            }
    
            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, 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 = (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;
        }
    }
    

    【讨论】:

    • 这看起来很有希望,但非常耗时。我会试试这个,否则我会放弃并要求使用 CSV 代替,(尽管我怀疑我会得到那么幸运)。感谢您提供大量代码示例
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-26
    • 2011-12-21
    • 1970-01-01
    • 2018-04-19
    • 1970-01-01
    • 2021-12-22
    • 2019-02-24
    相关资源
    最近更新 更多