Fix compiler warnings about nullables

Remove obsolete support of .Net framework 2.0
This commit is contained in:
Peter Kirmeier 2023-04-16 20:45:21 +02:00
parent c9ce4d1d21
commit 1d29fea766

View file

@ -1,480 +1,497 @@
// <copyright file="JsonParser.cs" company="PlaceholderCompany"> // <copyright file="JsonParser.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved. // Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright> // </copyright>
namespace SystemTrayMenu.Helpers.Updater namespace SystemTrayMenu.Helpers.Updater
{ {
// Copyright (c) 2018 Alex Parker // The MIT License (MIT)
// Copyright (c) 2018-2019 Peter Kirmeier
// Copyright (c) 2018 Alex Parker
// Permission is hereby granted, free of charge, to any person obtaining a copy of // Copyright (c) 2018-2023 Peter Kirmeier
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to // Permission is hereby granted, free of charge, to any person obtaining a copy of
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // this software and associated documentation files (the "Software"), to deal in
// the Software, and to permit persons to whom the Software is furnished to do so, // the Software without restriction, including without limitation the rights to
// subject to the following conditions: // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// The above copyright notice and this permission notice shall be included in all // subject to the following conditions:
// copies or substantial portions of the Software.
// The above copyright notice and this permission notice shall be included in all
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // copies or substantial portions of the Software.
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
using System; // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
using System.Collections; // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System.Collections.Generic; using System;
using System.Reflection; using System.Collections;
using System.Runtime.Serialization; using System.Collections.Generic;
using System.Text; using System.Reflection;
using System.Runtime.Serialization;
// Really simple JSON parser in ~300 lines using System.Text;
// - Attempts to parse JSON files with minimal GC allocation
// - Nice and simple "[1,2,3]".FromJson<List<int>>() API // Really simple JSON parser in ~300 lines
// - Classes and structs can be parsed too! // - Attempts to parse JSON files with minimal GC allocation
// class Foo { public int Value; } // - Nice and simple "[1,2,3]".FromJson<List<int>>() API
// "{\"Value\":10}".FromJson<Foo>() // - Classes and structs can be parsed too!
// - Can parse JSON without type information into Dictionary<string,object> and List<object> e.g. // class Foo { public int Value; }
// "[1,2,3]".FromJson<object>().GetType() == typeof(List<object>) // "{\"Value\":10}".FromJson<Foo>()
// "{\"Value\":10}".FromJson<object>().GetType() == typeof(Dictionary<string,object>) // - Can parse JSON without type information into Dictionary<string,object> and List<object> e.g.
// - No JIT Emit support to support AOT compilation on iOS // "[1,2,3]".FromJson<object>().GetType() == typeof(List<object>)
// - Attempts are made to NOT throw an exception if the JSON is corrupted or invalid: returns null instead. // "{\"Value\":10}".FromJson<object>().GetType() == typeof(Dictionary<string,object>)
// - Only public fields and property setters on classes/structs will be written to // - No JIT Emit support to support AOT compilation on iOS
// // - Attempts are made to NOT throw an exception if the JSON is corrupted or invalid: returns null instead.
// Limitations: // - Only public fields and property setters on classes/structs will be written to
// - No JIT Emit support to parse structures quickly //
// - Limited to parsing <2GB JSON files (due to int.MaxValue) // Limitations:
// - Parsing of abstract classes or interfaces is NOT supported and will throw an exception. // - No JIT Emit support to parse structures quickly
public static class JSONParser // - Limited to parsing <2GB JSON files (due to int.MaxValue)
{ // - Parsing of abstract classes or interfaces is NOT supported and will throw an exception.
[ThreadStatic] public static class JSONParser
private static Stack<List<string>> splitArrayPool; {
[ThreadStatic] [ThreadStatic]
private static StringBuilder stringBuilder; private static Stack<List<string>>? splitArrayPool;
[ThreadStatic] [ThreadStatic]
private static Dictionary<Type, Dictionary<string, FieldInfo>> fieldInfoCache; private static StringBuilder? stringBuilder;
[ThreadStatic] [ThreadStatic]
private static Dictionary<Type, Dictionary<string, PropertyInfo>> propertyInfoCache; private static Dictionary<Type, Dictionary<string, FieldInfo>>? fieldInfoCache;
[ThreadStatic]
public static T FromJson<T>(this string json) private static Dictionary<Type, Dictionary<string, PropertyInfo>>? propertyInfoCache;
{
// Initialize, if needed, the ThreadStatic variables public static T? FromJson<T>(this string json)
propertyInfoCache ??= new Dictionary<Type, Dictionary<string, PropertyInfo>>(); {
// Initialize, if needed, the ThreadStatic variables
fieldInfoCache ??= new Dictionary<Type, Dictionary<string, FieldInfo>>(); propertyInfoCache ??= new Dictionary<Type, Dictionary<string, PropertyInfo>>();
fieldInfoCache ??= new Dictionary<Type, Dictionary<string, FieldInfo>>();
stringBuilder ??= new StringBuilder(); stringBuilder ??= new StringBuilder();
splitArrayPool ??= new Stack<List<string>>();
splitArrayPool ??= new Stack<List<string>>();
// Remove all whitespace not within strings to make parsing simpler
// Remove all whitespace not within strings to make parsing simpler stringBuilder.Length = 0;
stringBuilder.Length = 0; for (int i = 0; i < json.Length; i++)
for (int i = 0; i < json.Length; i++) {
{ char c = json[i];
char c = json[i]; if (c == '"')
if (c == '"') {
{ i = AppendUntilStringEnd(true, i, json);
i = AppendUntilStringEnd(true, i, json); continue;
continue; }
}
if (char.IsWhiteSpace(c))
if (char.IsWhiteSpace(c)) {
{ continue;
continue; }
}
stringBuilder.Append(c);
stringBuilder.Append(c); }
}
// Parse the thing!
// Parse the thing! return (T?)ParseValue(typeof(T), stringBuilder.ToString());
return (T)ParseValue(typeof(T), stringBuilder.ToString()); }
}
internal static object? ParseValue(Type type, string json)
internal static object ParseValue(Type type, string json) {
{ // Initialize, if needed, the ThreadStatic variables
if (type == typeof(string)) propertyInfoCache ??= new Dictionary<Type, Dictionary<string, PropertyInfo>>();
{ fieldInfoCache ??= new Dictionary<Type, Dictionary<string, FieldInfo>>();
if (json.Length <= 2) stringBuilder ??= new StringBuilder();
{ splitArrayPool ??= new Stack<List<string>>();
return string.Empty;
} if (type == typeof(string))
{
StringBuilder parseStringBuilder = new(json.Length); if (json.Length <= 2)
for (int i = 1; i < json.Length - 1; ++i) {
{ return string.Empty;
if (json[i] == '\\' && i + 1 < json.Length - 1) }
{
int j = "\"\\nrtbf/".IndexOf(json[i + 1]); StringBuilder parseStringBuilder = new(json.Length);
if (j >= 0) for (int i = 1; i < json.Length - 1; ++i)
{ {
parseStringBuilder.Append("\"\\\n\r\t\b\f/"[j]); if (json[i] == '\\' && i + 1 < json.Length - 1)
++i; {
continue; int j = "\"\\nrtbf/".IndexOf(json[i + 1]);
} if (j >= 0)
{
if (json[i + 1] == 'u' && i + 5 < json.Length - 1) parseStringBuilder.Append("\"\\\n\r\t\b\f/"[j]);
{ ++i;
if (uint.TryParse(json.AsSpan(i + 2, 4), System.Globalization.NumberStyles.AllowHexSpecifier, null, out uint c)) continue;
{ }
parseStringBuilder.Append((char)c);
i += 5; if (json[i + 1] == 'u' && i + 5 < json.Length - 1)
continue; {
} if (uint.TryParse(json.AsSpan(i + 2, 4), System.Globalization.NumberStyles.AllowHexSpecifier, null, out uint c))
} {
} parseStringBuilder.Append((char)c);
i += 5;
parseStringBuilder.Append(json[i]); continue;
} }
}
return parseStringBuilder.ToString(); }
}
parseStringBuilder.Append(json[i]);
if (type.IsPrimitive) }
{
var result = Convert.ChangeType(json, type, System.Globalization.CultureInfo.InvariantCulture); return parseStringBuilder.ToString();
return result; }
}
if (type.IsPrimitive)
if (type == typeof(decimal)) {
{ var result = Convert.ChangeType(json, type, System.Globalization.CultureInfo.InvariantCulture);
decimal.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out decimal result); return result;
return result; }
}
if (type == typeof(decimal))
if (json == "null") {
{ decimal.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out decimal result);
return null; return result;
} }
if (type.IsEnum) if (json == "null")
{ {
if (json[0] == '"') return null;
{ }
json = json[1..^1];
} if (type.IsEnum)
{
try if (json[0] == '"')
{ {
return Enum.Parse(type, json, false); json = json[1..^1];
} }
catch
{ try
return 0; {
} return Enum.Parse(type, json, false);
} }
catch
if (type.IsArray) {
{ return 0;
Type arrayType = type.GetElementType(); }
if (json[0] != '[' || json[^1] != ']') }
{
return null; if (type.IsArray)
} {
Type arrayType = type.GetElementType() !;
List<string> elems = Split(json); if (json[0] != '[' || json[^1] != ']')
Array newArray = Array.CreateInstance(arrayType, elems.Count); {
for (int i = 0; i < elems.Count; i++) return null;
{ }
newArray.SetValue(ParseValue(arrayType, elems[i]), i);
} List<string> elems = Split(json);
Array newArray = Array.CreateInstance(arrayType, elems.Count);
splitArrayPool.Push(elems); for (int i = 0; i < elems.Count; i++)
return newArray; {
} newArray.SetValue(ParseValue(arrayType, elems[i]), i);
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{ splitArrayPool.Push(elems);
Type listType = type.GetGenericArguments()[0]; return newArray;
if (json[0] != '[' || json[^1] != ']') }
{
return null; if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
} {
Type listType = type.GetGenericArguments()[0];
List<string> elems = Split(json); if (json[0] != '[' || json[^1] != ']')
var list = (IList)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count }); {
for (int i = 0; i < elems.Count; i++) return null;
{ }
list.Add(ParseValue(listType, elems[i]));
} List<string> elems = Split(json);
var list = (IList?)type.GetConstructor(new Type[] { typeof(int) })?.Invoke(new object[] { elems.Count });
splitArrayPool.Push(elems); if (list == null)
return list; {
} return null;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{ for (int i = 0; i < elems.Count; i++)
Type keyType, valueType; {
{ list.Add(ParseValue(listType, elems[i]));
Type[] args = type.GetGenericArguments(); }
keyType = args[0];
valueType = args[1]; splitArrayPool.Push(elems);
} return list;
}
// Refuse to parse dictionary keys that aren't of type string
if (keyType != typeof(string)) if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{ {
return null; Type keyType, valueType;
} {
Type[] args = type.GetGenericArguments();
// Must be a valid dictionary element keyType = args[0];
if (json[0] != '{' || json[^1] != '}') valueType = args[1];
{ }
return null;
} // Refuse to parse dictionary keys that aren't of type string
if (keyType != typeof(string))
// The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON {
List<string> elems = Split(json); return null;
if (elems.Count % 2 != 0) }
{
return null; // Must be a valid dictionary element
} if (json[0] != '{' || json[^1] != '}')
{
var dictionary = (IDictionary)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count / 2 }); return null;
for (int i = 0; i < elems.Count; i += 2) }
{
if (elems[i].Length <= 2) // The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
{ List<string> elems = Split(json);
continue; if (elems.Count % 2 != 0)
} {
return null;
string keyValue = elems[i][1..^1]; }
object val = ParseValue(valueType, elems[i + 1]);
dictionary.Add(keyValue, val); var dictionary = (IDictionary?)type.GetConstructor(new Type[] { typeof(int) })?.Invoke(new object[] { elems.Count / 2 });
} if (dictionary == null)
{
return dictionary; return null;
} }
if (type == typeof(object)) for (int i = 0; i < elems.Count; i += 2)
{ {
return ParseAnonymousValue(json); if (elems[i].Length <= 2)
} {
continue;
if (json[0] == '{' && json[^1] == '}') }
{
return ParseObject(type, json); string keyValue = elems[i][1..^1];
} object? val = ParseValue(valueType, elems[i + 1]);
dictionary.Add(keyValue, val);
return null; }
}
return dictionary;
private static int AppendUntilStringEnd(bool appendEscapeCharacter, int startIdx, string json) }
{
stringBuilder.Append(json[startIdx]); if (type == typeof(object))
for (int i = startIdx + 1; i < json.Length; i++) {
{ return ParseAnonymousValue(json);
if (json[i] == '\\') }
{
if (appendEscapeCharacter) if (json[0] == '{' && json[^1] == '}')
{ {
stringBuilder.Append(json[i]); return ParseObject(type, json);
} }
stringBuilder.Append(json[i + 1]); return null;
i++; // Skip next character as it is escaped }
}
else if (json[i] == '"') private static int AppendUntilStringEnd(bool appendEscapeCharacter, int startIdx, string json)
{ {
stringBuilder.Append(json[i]); stringBuilder!.Append(json[startIdx]);
return i; for (int i = startIdx + 1; i < json.Length; i++)
} {
else if (json[i] == '\\')
{ {
stringBuilder.Append(json[i]); if (appendEscapeCharacter)
} {
} stringBuilder.Append(json[i]);
}
return json.Length - 1;
} stringBuilder.Append(json[i + 1]);
i++; // Skip next character as it is escaped
// Splits { <value>:<value>, <value>:<value> } and [ <value>, <value> ] into a list of <value> strings }
private static List<string> Split(string json) else if (json[i] == '"')
{ {
List<string> splitArray = splitArrayPool.Count > 0 ? splitArrayPool.Pop() : new List<string>(); stringBuilder.Append(json[i]);
splitArray.Clear(); return i;
if (json.Length == 2) }
{ else
return splitArray; {
} stringBuilder.Append(json[i]);
}
int parseDepth = 0; }
stringBuilder.Length = 0;
for (int i = 1; i < json.Length - 1; i++) return json.Length - 1;
{ }
switch (json[i])
{ // Splits { <value>:<value>, <value>:<value> } and [ <value>, <value> ] into a list of <value> strings
case '[': private static List<string> Split(string json)
case '{': {
parseDepth++; List<string> splitArray = splitArrayPool!.Count > 0 ? splitArrayPool.Pop() : new List<string>();
break; splitArray.Clear();
case ']': if (json.Length == 2)
case '}': {
parseDepth--; return splitArray;
break; }
case '"':
i = AppendUntilStringEnd(true, i, json); int parseDepth = 0;
continue; stringBuilder!.Length = 0;
case ',': for (int i = 1; i < json.Length - 1; i++)
case ':': {
if (parseDepth == 0) switch (json[i])
{ {
splitArray.Add(stringBuilder.ToString()); case '[':
stringBuilder.Length = 0; case '{':
continue; parseDepth++;
} break;
case ']':
break; case '}':
} parseDepth--;
break;
stringBuilder.Append(json[i]); case '"':
} i = AppendUntilStringEnd(true, i, json);
continue;
splitArray.Add(stringBuilder.ToString()); case ',':
case ':':
return splitArray; if (parseDepth == 0)
} {
splitArray.Add(stringBuilder.ToString());
private static object ParseAnonymousValue(string json) stringBuilder.Length = 0;
{ continue;
if (json.Length == 0) }
{
return null; break;
} }
if (json[0] == '{' && json[^1] == '}') stringBuilder.Append(json[i]);
{ }
List<string> elems = Split(json);
if (elems.Count % 2 != 0) splitArray.Add(stringBuilder.ToString());
{
return null; return splitArray;
} }
var dict = new Dictionary<string, object>(elems.Count / 2); private static object? ParseAnonymousValue(string json)
for (int i = 0; i < elems.Count; i += 2) {
{ if (json.Length == 0)
dict.Add(elems[i][1..^1], ParseAnonymousValue(elems[i + 1])); {
} return null;
}
return dict;
} if (json[0] == '{' && json[^1] == '}')
{
if (json[0] == '[' && json[^1] == ']') List<string> elems = Split(json);
{ if (elems.Count % 2 != 0)
List<string> items = Split(json); {
var finalList = new List<object>(items.Count); return null;
for (int i = 0; i < items.Count; i++) }
{
finalList.Add(ParseAnonymousValue(items[i])); var dict = new Dictionary<string, object?>(elems.Count / 2);
} for (int i = 0; i < elems.Count; i += 2)
{
return finalList; dict.Add(elems[i][1..^1], ParseAnonymousValue(elems[i + 1]));
} }
if (json[0] == '"' && json[^1] == '"') return dict;
{ }
return ParseValue(typeof(string), json); // fix https://github.com/zanders3/json/issues/29
} if (json[0] == '[' && json[^1] == ']')
{
if (char.IsDigit(json[0]) || json[0] == '-') List<string> items = Split(json);
{ var finalList = new List<object?>(items.Count);
if (json.Contains('.')) for (int i = 0; i < items.Count; i++)
{ {
double.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out double result); finalList.Add(ParseAnonymousValue(items[i]));
return result; }
}
else return finalList;
{ }
_ = int.TryParse(json, out int result);
return result; if (json[0] == '"' && json[^1] == '"')
} {
} return ParseValue(typeof(string), json); // fix https://github.com/zanders3/json/issues/29
}
if (json == "true")
{ if (char.IsDigit(json[0]) || json[0] == '-')
return true; {
} if (json.Contains('.'))
{
if (json == "false") double.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out double result);
{ return result;
return false; }
} else
{
// handles json == "null" as well as invalid JSON _ = int.TryParse(json, out int result);
return null; return result;
} }
}
private static Dictionary<string, T> CreateMemberNameDictionary<T>(T[] members)
where T : MemberInfo if (json == "true")
{ {
Dictionary<string, T> nameToMember = new(StringComparer.OrdinalIgnoreCase); return true;
for (int i = 0; i < members.Length; i++) }
{
/*T member = members[i]; if (json == "false")
if (member.IsDefined(typeof(IgnoreDataMemberAttribute), true)) {
continue; return false;
}
string name = member.Name;
if (member.IsDefined(typeof(DataMemberAttribute), true)) // handles json == "null" as well as invalid JSON
{ return null;
DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true); }
if (!string.IsNullOrEmpty(dataMemberAttribute.Name))
name = dataMemberAttribute.Name; private static Dictionary<string, T> CreateMemberNameDictionary<T>(T[] members)
} where T : MemberInfo
{
nameToMember.Add(name, member);*/ Dictionary<string, T> nameToMember = new(StringComparer.OrdinalIgnoreCase);
// The above code is not working with .Net framework 2.0, so we ignore these attributes for compatibility reasons: for (int i = 0; i < members.Length; i++)
nameToMember.Add(members[i].Name, members[i]); {
} T member = members[i];
if (member.IsDefined(typeof(IgnoreDataMemberAttribute), true))
return nameToMember; {
} continue;
}
private static object ParseObject(Type type, string json)
{ string name = member.Name;
object instance = FormatterServices.GetUninitializedObject(type); if (member.IsDefined(typeof(DataMemberAttribute), true))
{
// The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON DataMemberAttribute? dataMemberAttribute = (DataMemberAttribute?)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true);
List<string> elems = Split(json); if (!string.IsNullOrEmpty(dataMemberAttribute?.Name))
if (elems.Count % 2 != 0) {
{ name = dataMemberAttribute.Name;
return instance; }
} }
if (!fieldInfoCache.TryGetValue(type, out Dictionary<string, FieldInfo> nameToField)) nameToMember.Add(name, member);
{ }
nameToField = CreateMemberNameDictionary(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
fieldInfoCache.Add(type, nameToField); return nameToMember;
} }
if (!propertyInfoCache.TryGetValue(type, out Dictionary<string, PropertyInfo> nameToProperty)) private static object ParseObject(Type type, string json)
{ {
nameToProperty = CreateMemberNameDictionary(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy)); object instance = FormatterServices.GetUninitializedObject(type);
propertyInfoCache.Add(type, nameToProperty);
} // The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
List<string> elems = Split(json);
for (int i = 0; i < elems.Count; i += 2) if (elems.Count % 2 != 0)
{ {
if (elems[i].Length <= 2) return instance;
{ }
continue;
} if (!fieldInfoCache!.TryGetValue(type, out Dictionary<string, FieldInfo>? nameToField))
{
string key = elems[i][1..^1]; nameToField = CreateMemberNameDictionary(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
string value = elems[i + 1]; fieldInfoCache.Add(type, nameToField);
}
if (nameToField.TryGetValue(key, out FieldInfo fieldInfo))
{ if (!propertyInfoCache!.TryGetValue(type, out Dictionary<string, PropertyInfo>? nameToProperty))
fieldInfo.SetValue(instance, ParseValue(fieldInfo.FieldType, value)); {
} nameToProperty = CreateMemberNameDictionary(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
else if (nameToProperty.TryGetValue(key, out PropertyInfo propertyInfo)) propertyInfoCache.Add(type, nameToProperty);
{ }
propertyInfo.SetValue(instance, ParseValue(propertyInfo.PropertyType, value), null);
} for (int i = 0; i < elems.Count; i += 2)
} {
if (elems[i].Length <= 2)
return instance; {
} continue;
} }
string key = elems[i][1..^1];
string value = elems[i + 1];
if (nameToField.TryGetValue(key, out FieldInfo? fieldInfo))
{
fieldInfo.SetValue(instance, ParseValue(fieldInfo.FieldType, value));
}
else if (nameToProperty.TryGetValue(key, out PropertyInfo? propertyInfo))
{
propertyInfo.SetValue(instance, ParseValue(propertyInfo.PropertyType, value), null);
}
}
return instance;
}
}
} }