对于 Blazor 控件中的某些动态数据绑定(bind),我必须使用如下模型:
public class DynamicDataGridResult
{
public DynamicDataGridResult()
{
Columns = new Dictionary<string, ColumnDefinition>();
Data = new List<Dictionary<string, object>>();
}
public Dictionary<string, ColumnDefinition> Columns { get; set; }
public List<Dictionary<string, object>> Data { get; set; }
}
public class ColumnDefinition
{
public string PropertyName { get; set; }
public string TypeFullName { get; set; }
public string Title { get; set; }
public string? Formatter { get; set; }
public Type GetColumnType()
{
var type = Type.GetType(TypeFullName);
return type;
}
}
在“列”中,我描述了我的列(用于直接以 .Net 方式显示的 TypeFullName、用于微调 ToString(xx) 的格式化程序等)。
在数据中,我可以输入我的值。
这是两者的工作示例(下面的代码仅用于示例目的,以示例数据进行说明,但对我的问题并不重要,如果您想跳过,请参阅下文):
DynamicDataGridResult dataGridModel = new();
var columns = new List<ColumnDefinition>()
{
new() { TypeFullName = typeof(int).FullName!, PropertyName = "EmployeeID", Title = "Employee ID"},
new() { TypeFullName = typeof(decimal?).FullName!, PropertyName = "Rating", Title = "Rating (Nullable<decimal>)" },
new() { TypeFullName = typeof(string).FullName!, PropertyName = "FirstName", Title = "First name"},
new() { TypeFullName = typeof(string).FullName!, PropertyName = "LastName", Title = "Last name"},
new() { TypeFullName = typeof(DateTime).FullName!, PropertyName ="HireDate", Title = "Hire date"},
};
columns.ForEach(c =>
{
if (c.TypeFullName.Contains("Decimal", StringComparison.OrdinalIgnoreCase)
|| c.TypeFullName.Contains("Double", StringComparison.OrdinalIgnoreCase)
|| c.TypeFullName.Contains("Single", StringComparison.OrdinalIgnoreCase))
{
c.Formatter = "{0:N2}";
}
});
dataGridModel.Columns = columns.ToDictionary(k => k.PropertyName);
dataGridModel.Data = Enumerable.Range(0, 25).Select(i =>
{
var row = new Dictionary<string, object>();
foreach (var column in dataGridModel.Columns)
{
object value;
if (column.Value.TypeFullName == typeof(int).FullName)
value = i;
else if (column.Value.TypeFullName == typeof(decimal?).FullName)
value = i % 3 != 0 ? ((double)i / 2.1234) : null;
else if (column.Value.TypeFullName == typeof(DateTime).FullName)
value = DateTime.Now.AddMonths(i);
else
value = $"{column.Key}{i}";
row.Add(column.Key, value);
}
return row;
}).ToList();
我相信你明白了。
如果我直接将控件与这些对象绑定(bind),就可以了。
但是,如果我通过 System.Text.Json 的 Http.PostAsJsonAsync(url)/ReadFromJsonAsync() 序列化/反序列化它们,它就会被破坏,因为我所有的“对象”都变成了一些“JsonElement”。 我当然理解这种行为,现在我必须处理它。
所以我的问题是:如果我在另一个属性中有类型名称,如何将它们反序列化为常规 .Net 对象?
我搜索创建一个自定义 JsonConverter,但这是一个巨大的野兽,有很多子类和内部/密封类,所以我能够覆盖它。
或者,我在反序列化后做了其他一些事情来“重新计算”我的字典。
下面是转换方法的一部分:
private IEnumerable<Dictionary<string, object>> JsonElementAsObjects()
{
var liste = new List<Dictionary<string, object>>();
foreach (var row in GridResult.Data)
{
var dictionary = new Dictionary<string, object>();
foreach (var col in row)
{
var columnDefinition = Columns[col.Key];
object value = null;
if (col.Value != null)
{
value = GetObject((JsonElement) col.Value, columnDefinition);
}
dictionary.Add(col.Key, value);
}
liste.Add(dictionary);
}
return liste;
}
我试图在 JsonElement/JsonValue 中找到一个方法将其转换为对象,但我只找到了一个通用方法( ConvertJsonElement<TypeToConvert>()
中的 JsonValue<T>
这并没有真正帮助,因为我的类型仅在运行时已知) .
所以我临时提取了其中的一些内容,并将其调整为使用运行时类型:
private object GetObject(JsonElement element, ColumnDefinition columnDefinition)
{
var typeToConvert = columnDefinition.GetColumnType();
switch (element.ValueKind)
{
case JsonValueKind.Number:
if (typeToConvert == typeof(int) || typeToConvert == typeof(int?))
{
return element.GetInt32();
}
if (typeToConvert == typeof(long) || typeToConvert == typeof(long?))
{
return element.GetInt64();
}
if (typeToConvert == typeof(double) || typeToConvert == typeof(double?))
{
return element.GetDouble();
}
if (typeToConvert == typeof(short) || typeToConvert == typeof(short?))
{
return element.GetInt16();
}
if (typeToConvert == typeof(decimal) || typeToConvert == typeof(decimal?))
{
return element.GetDecimal();
}
if (typeToConvert == typeof(byte) || typeToConvert == typeof(byte?))
{
return element.GetByte();
}
if (typeToConvert == typeof(float) || typeToConvert == typeof(float?))
{
return element.GetSingle();
}
if (typeToConvert == typeof(uint) || typeToConvert == typeof(uint?))
{
return element.GetUInt32();
}
if (typeToConvert == typeof(ushort) || typeToConvert == typeof(ushort?))
{
return element.GetUInt16();
}
if (typeToConvert == typeof(ulong) || typeToConvert == typeof(ulong?))
{
return element.GetUInt64();
}
if (typeToConvert == typeof(sbyte) || typeToConvert == typeof(sbyte?))
{
return element.GetSByte();
}
break;
case JsonValueKind.String:
if (typeToConvert == typeof(string))
{
return element.GetString()!;
}
if (typeToConvert == typeof(DateTime) || typeToConvert == typeof(DateTime?))
{
return element.GetDateTime();
}
if (typeToConvert == typeof(DateTimeOffset) || typeToConvert == typeof(DateTimeOffset?))
{
return element.GetDateTimeOffset();
}
if (typeToConvert == typeof(Guid) || typeToConvert == typeof(Guid?))
{
return element.GetGuid();
}
if (typeToConvert == typeof(char) || typeToConvert == typeof(char?))
{
string? str = element.GetString();
Debug.Assert(str != null);
if (str.Length == 1)
{
return str[0];
}
}
break;
case JsonValueKind.True:
case JsonValueKind.False:
if (typeToConvert == typeof(bool) || typeToConvert == typeof(bool?))
{
return element.GetBoolean();
}
break;
}
return element;
}
它与此完美配合。
但是这显然是一个繁重无用的后期处理,需要维护大量样板代码。
您能否给我一个更清晰的解决方案,将我的数据反序列化为“真实对象”,或者至少像我一样在反序列化后调整它们,但使用更短的代码,使用 System.Text.Json 中的一些隐藏技巧?
谢谢!
最佳答案
快速而肮脏的方法是将 UnknownTypeHandling
设置为 JsonUnknownTypeHandling.JsonNode
进行临时反序列化(就像您所做的那样),然后使用非通用 JsonNode .反序列化
。类似于以下内容:
var res = JsonSerializer.Deserialize<DynamicDataGridResult>(jsonString, new JsonSerializerOptions
{
UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode
});
res = new DynamicDataGridResult
{
Columns = res.Columns,
Data = res.Data
.Select(d => d
.ToDictionary(
d => d.Key,
d =>(d.Value as JsonNode)?.Deserialize(res.Columns[d.Key].GetColumnType())))
.ToList()
};
或者使用 JsonElement
进行相同的操作:
var res = JsonSerializer.Deserialize<DynamicDataGridResult>(jsonString);
res = new DynamicDataGridResult
{
Columns = res.Columns,
Data = res.Data
.Select(d => d
.ToDictionary(
d => d.Key,
d =>(d.Value as JsonElement?)?.Deserialize(res.Columns[d.Key].GetColumnType())))
.ToList()
};
关于c# - System.Text.Json 中的动态类型处理/"object"属性的自定义反序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76765471/