反序列化 JsonValue 以在 System.Json 中建模
Deserialize JsonValue to model in System.Json
在任何人开始建议任何像 Newtonsoft.Json
、System.Text.Json
或任何其他我喜欢使用的好东西和简单的东西之前,请知道我无法使用 [=14= 以外的任何东西],因为我在一个应用程序的约束下工作,(我正在制作一个插件),我没有影响并且已经停止了积极的开发,(它是一个 ERP 系统,每年只有一次安全补丁,并且功能请求导致被动攻击性反应;即使我提出自己免费进行更改)。
我有一些 json,还有一些不错的领域模型,(对象,类,模型,实体,任何你喜欢用属性调用 public 类 ), 我希望他们结婚。当有嵌套时使用反射是一种痛苦。
任何人都可以告诉我一些不需要任何 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,但是你需要使用反射的 属性 映射,这段代码就在那里。
让我知道这是否适合您,因为它在重用反射类型等方面进行了一些优化。
我认为您需要测试 属性 是否为 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<System.String>.</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;
}
}
在任何人开始建议任何像 Newtonsoft.Json
、System.Text.Json
或任何其他我喜欢使用的好东西和简单的东西之前,请知道我无法使用 [=14= 以外的任何东西],因为我在一个应用程序的约束下工作,(我正在制作一个插件),我没有影响并且已经停止了积极的开发,(它是一个 ERP 系统,每年只有一次安全补丁,并且功能请求导致被动攻击性反应;即使我提出自己免费进行更改)。
我有一些 json,还有一些不错的领域模型,(对象,类,模型,实体,任何你喜欢用属性调用 public 类 ), 我希望他们结婚。当有嵌套时使用反射是一种痛苦。
任何人都可以告诉我一些不需要任何 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,但是你需要使用反射的 属性 映射,这段代码就在那里。
让我知道这是否适合您,因为它在重用反射类型等方面进行了一些优化。
我认为您需要测试 属性 是否为 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<System.String>.</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;
}
}