反序列化具有不同类型的 GenericObject<T> 列表
Deserializing a list of GenericObject<T> with varying types
我的最终目标是存储一些用于生成一些 SQL 用于报告的参数,以试图减少(或消除?)SQL 注入的可能性。
因此,在我的部分解决方案中,我计划在我的应用程序的管理面板中创建报告定义。报告定义是用于生成输出的原始 SQL,然后有一个参数定义在 UI 中呈现给用户并尝试保持强类型。
参数是我目前卡住的地方。我可以正确地序列化参数列表并且 XML 看起来像我期望的那样,但尝试反序列化结果会出现一些异常。
我试图构建一个简单的方法来将 XML 字符串转换为一个对象并得到了一些 yuk。我明白异常和原因,就是不知道怎么解决。
当我尝试反序列化一个通用对象时,它失败了。在 Xml 中,泛型可能如下所示:
<?xml version="1.0" encoding="utf-16"?>
<ReportParameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Parameters>
<ReportParameter xsi:type="ReportParameterOfInt32">
<Name>Test</Name>
<Value xsi:type="xsd:int">4</Value>
</ReportParameter>
<ReportParameter xsi:type="ReportParameterOfDateTime">
<Name>Startdate</Name>
<Value xsi:type="xsd:dateTime">2019-04-05T22:52:25.9869948-05:00</Value>
</ReportParameter>
</Parameters>
</ReportParameters>
我认为 ReportParameter 上的类型是我的挂断。 xsi:type="ReportParameterOfInt32"
应解析为 ReportParameter(或类似)
这就是我必须serialize/deserialize我在说什么。
public class ReportParameter
{
/// <summary>
/// All names are prefixed automatically with the @ sign.
/// This is the name that will appear in the query when building/writing
/// </summary>
public string Name { get; set; }
public object Value { get; set; }
}
/// <summary>
/// A generic object type that can hold many data types
/// </summary>
/// <typeparam name="T"></typeparam>
public class ReportParameter<T> : ReportParameter
{
T _value;
[XmlIgnore]
public new T Value
{
get { return _value; }
set
{
base.Value = value;
_value = value;
}
}
}
然后我有一个扩展方法来序列化数据(最终将写入 table)
public static string Serialize(this ReportParameters parameters)
{
List<Type> types = new List<Type>();
// Loop over all the parameters
foreach (var a in parameters.Parameters)
{
// See if this Type is already in the list of potential types
if (types.Contains(a.GetType()))
{
continue;
}
// Add it to the List
types.Add(a.GetType());
}
types.Add(typeof(ReportParameters));
Type[] genericTypes = types.ToArray();
XmlSerializer xsSubmit = new XmlSerializer(typeof(ReportParameters), genericTypes);
string xml = string.Empty;
using (var sww = new StringWriter())
{
using (var writer = XmlWriter.Create(sww))
{
xsSubmit.Serialize(writer, parameters);
xml = sww.ToString();
return xml;
}
}
}
在编写了一个测试方法来进行简单的反序列化之后,我得到了一个可爱的异常:
Message: Test method X.Tests.TestSerialize.SerializeGeneric threw exception:
System.InvalidOperationException: There is an error in XML document (1, 170). ---> System.InvalidOperationException: The specified type was not recognized: name='ReportParameterOfInt32', namespace='', at .
如有任何帮助,我们将不胜感激。
很遗憾,我有适合您的解决方案,但不符合您的期望。我不知道你是否尝试过这个,但我希望这至少能对你有所帮助。
如果我错了请纠正我,但除非您推出自己的反序列化器,否则似乎没有对您要完成的工作的支持。
可能缺少一种方法来通知 Xml 序列化程序使用通用 class ReportParameter<T>
以及它应该考虑哪些类型的 T
。
因此,我没有使用通用参数方法,而是显式地手动滚动了那些 classes。
public class ReportParameterOfInt32 : ReportParameter
{
int _value;
[XmlIgnore]
public new int Value
{
get { return _value; }
set
{
base.Value = value;
_value = value;
}
}
}
public class ReportParameterOfDateTime : ReportParameter
{
DateTime _value;
[XmlIgnore]
public new DateTime Value
{
get { return _value; }
set
{
base.Value = value;
_value = value;
}
}
}
更新了 ReportParameters
(我猜它的样子,因为它没有在你的问题中提供):
public class ReportParameters
{
[XmlArrayItem(typeof(ReportParameterOfInt32))]
[XmlArrayItem(typeof(ReportParameterOfDateTime))]
public ReportParameter[] Parameters { get; set; }
}
简化了序列化器并编写了反序列化器:
private static string Serialize(ReportParameters parameters)
{
XmlSerializer xsSubmit = new XmlSerializer(typeof(ReportParameters));
string xml = string.Empty;
using (var sww = new StringWriter())
{
using (var writer = XmlWriter.Create(sww))
{
xsSubmit.Serialize(writer, parameters);
xml = sww.ToString();
return xml;
}
}
}
private static ReportParameters Deserialize(string xml)
{
XmlSerializer xsSubmit = new XmlSerializer(typeof(ReportParameters));
using (var reader = new StringReader(xml))
{
return (ReportParameters)xsSubmit.Deserialize(reader);
}
}
编写测试代码,然后按预期执行:
var xml = Serialize(
new ReportParameters
{
Parameters = new ReportParameter[]
{
new ReportParameterOfInt32 { Name = "Test", Value = 4 },
new ReportParameterOfDateTime { Name = "Startdate", Value = new DateTime(2019, 04, 05, 22, 52, 25) }
}
});
var obj = Deserialize(xml);
制作中:
<?xml version="1.0" encoding="utf-16"?>
<ReportParameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Parameters>
<ReportParameterOfInt32>
<Name>Test</Name>
<Value xsi:type="xsd:int">4</Value>
</ReportParameterOfInt32>
<ReportParameterOfDateTime>
<Name>Startdate</Name>
<Value xsi:type="xsd:dateTime">2019-04-05T22:52:25</Value>
</ReportParameterOfDateTime>
</Parameters>
</ReportParameters>
我知道你可能会感到有点失望,但是,我确实认为你可以只使用包装器来确保它正确地写入底层类型,而不必序列化应该希望满足你的通用类型 "SQL Injection" 要求,但您仍然会遇到 strings 的问题。
您需要确保您 转义任何字符 ,这将使 SQL 注入成为可能,因为在构建 SQL 语句时,字符串仍然容易受到攻击发送到数据库。我相信你可以 google 如何做到这一点(因为我认为这是与你的问题完全不同的讨论)。
因此,您可以:
而不是新建一个新的 ReportParameter
private ReportParameter GetReportParameter<T>(string name, T value)
{
return new ReportParameter
{
Name = name,
Value = value
};
}
然后在序列化的时候:
var xml = Serialize(
new ReportParameters
{
Parameters = new ReportParameter[]
{
GetReportParameter("Test", 4),
GetReportParameter("Startdate", new DateTime(2019, 04, 05, 22, 52, 25))
}
});
现在,您不再需要 class 类型的 ReportParameterOfInt32
和 ReportParameterOfDateTime
。
至于想要反序列化 XML,如果您打算存储任何报表设计器信息,您可能需要在 ReportParameter
中添加一个单独的 Type
字段。但是,我不确切知道您的要求是什么,但它可能会帮助您在运行时以更简单的方式确定 Value
属性 的 Type
。
更新
@Patrick B 发现可以使用 XmlArrayItem
指定 ReportParameter
的通用类型版本来完成此操作。有道理。
示例:
public class ReportParameters
{
[XmlArrayItem(Type = typeof(ReportParameter<int>))]
[XmlArrayItem(Type = typeof(ReportParameter<int?>))]
[XmlArrayItem(Type = typeof(ReportParameter<DateTime>))]
public List<ReportParameter> Parameters { get; set; } = new List<ReportParameter>();
}
我的最终目标是存储一些用于生成一些 SQL 用于报告的参数,以试图减少(或消除?)SQL 注入的可能性。
因此,在我的部分解决方案中,我计划在我的应用程序的管理面板中创建报告定义。报告定义是用于生成输出的原始 SQL,然后有一个参数定义在 UI 中呈现给用户并尝试保持强类型。
参数是我目前卡住的地方。我可以正确地序列化参数列表并且 XML 看起来像我期望的那样,但尝试反序列化结果会出现一些异常。
我试图构建一个简单的方法来将 XML 字符串转换为一个对象并得到了一些 yuk。我明白异常和原因,就是不知道怎么解决。
当我尝试反序列化一个通用对象时,它失败了。在 Xml 中,泛型可能如下所示:
<?xml version="1.0" encoding="utf-16"?>
<ReportParameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Parameters>
<ReportParameter xsi:type="ReportParameterOfInt32">
<Name>Test</Name>
<Value xsi:type="xsd:int">4</Value>
</ReportParameter>
<ReportParameter xsi:type="ReportParameterOfDateTime">
<Name>Startdate</Name>
<Value xsi:type="xsd:dateTime">2019-04-05T22:52:25.9869948-05:00</Value>
</ReportParameter>
</Parameters>
</ReportParameters>
我认为 ReportParameter 上的类型是我的挂断。 xsi:type="ReportParameterOfInt32"
应解析为 ReportParameter(或类似)
这就是我必须serialize/deserialize我在说什么。
public class ReportParameter
{
/// <summary>
/// All names are prefixed automatically with the @ sign.
/// This is the name that will appear in the query when building/writing
/// </summary>
public string Name { get; set; }
public object Value { get; set; }
}
/// <summary>
/// A generic object type that can hold many data types
/// </summary>
/// <typeparam name="T"></typeparam>
public class ReportParameter<T> : ReportParameter
{
T _value;
[XmlIgnore]
public new T Value
{
get { return _value; }
set
{
base.Value = value;
_value = value;
}
}
}
然后我有一个扩展方法来序列化数据(最终将写入 table)
public static string Serialize(this ReportParameters parameters)
{
List<Type> types = new List<Type>();
// Loop over all the parameters
foreach (var a in parameters.Parameters)
{
// See if this Type is already in the list of potential types
if (types.Contains(a.GetType()))
{
continue;
}
// Add it to the List
types.Add(a.GetType());
}
types.Add(typeof(ReportParameters));
Type[] genericTypes = types.ToArray();
XmlSerializer xsSubmit = new XmlSerializer(typeof(ReportParameters), genericTypes);
string xml = string.Empty;
using (var sww = new StringWriter())
{
using (var writer = XmlWriter.Create(sww))
{
xsSubmit.Serialize(writer, parameters);
xml = sww.ToString();
return xml;
}
}
}
在编写了一个测试方法来进行简单的反序列化之后,我得到了一个可爱的异常:
Message: Test method X.Tests.TestSerialize.SerializeGeneric threw exception: System.InvalidOperationException: There is an error in XML document (1, 170). ---> System.InvalidOperationException: The specified type was not recognized: name='ReportParameterOfInt32', namespace='', at .
如有任何帮助,我们将不胜感激。
很遗憾,我有适合您的解决方案,但不符合您的期望。我不知道你是否尝试过这个,但我希望这至少能对你有所帮助。
如果我错了请纠正我,但除非您推出自己的反序列化器,否则似乎没有对您要完成的工作的支持。
可能缺少一种方法来通知 Xml 序列化程序使用通用 class ReportParameter<T>
以及它应该考虑哪些类型的 T
。
因此,我没有使用通用参数方法,而是显式地手动滚动了那些 classes。
public class ReportParameterOfInt32 : ReportParameter
{
int _value;
[XmlIgnore]
public new int Value
{
get { return _value; }
set
{
base.Value = value;
_value = value;
}
}
}
public class ReportParameterOfDateTime : ReportParameter
{
DateTime _value;
[XmlIgnore]
public new DateTime Value
{
get { return _value; }
set
{
base.Value = value;
_value = value;
}
}
}
更新了 ReportParameters
(我猜它的样子,因为它没有在你的问题中提供):
public class ReportParameters
{
[XmlArrayItem(typeof(ReportParameterOfInt32))]
[XmlArrayItem(typeof(ReportParameterOfDateTime))]
public ReportParameter[] Parameters { get; set; }
}
简化了序列化器并编写了反序列化器:
private static string Serialize(ReportParameters parameters)
{
XmlSerializer xsSubmit = new XmlSerializer(typeof(ReportParameters));
string xml = string.Empty;
using (var sww = new StringWriter())
{
using (var writer = XmlWriter.Create(sww))
{
xsSubmit.Serialize(writer, parameters);
xml = sww.ToString();
return xml;
}
}
}
private static ReportParameters Deserialize(string xml)
{
XmlSerializer xsSubmit = new XmlSerializer(typeof(ReportParameters));
using (var reader = new StringReader(xml))
{
return (ReportParameters)xsSubmit.Deserialize(reader);
}
}
编写测试代码,然后按预期执行:
var xml = Serialize(
new ReportParameters
{
Parameters = new ReportParameter[]
{
new ReportParameterOfInt32 { Name = "Test", Value = 4 },
new ReportParameterOfDateTime { Name = "Startdate", Value = new DateTime(2019, 04, 05, 22, 52, 25) }
}
});
var obj = Deserialize(xml);
制作中:
<?xml version="1.0" encoding="utf-16"?>
<ReportParameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Parameters>
<ReportParameterOfInt32>
<Name>Test</Name>
<Value xsi:type="xsd:int">4</Value>
</ReportParameterOfInt32>
<ReportParameterOfDateTime>
<Name>Startdate</Name>
<Value xsi:type="xsd:dateTime">2019-04-05T22:52:25</Value>
</ReportParameterOfDateTime>
</Parameters>
</ReportParameters>
我知道你可能会感到有点失望,但是,我确实认为你可以只使用包装器来确保它正确地写入底层类型,而不必序列化应该希望满足你的通用类型 "SQL Injection" 要求,但您仍然会遇到 strings 的问题。 您需要确保您 转义任何字符 ,这将使 SQL 注入成为可能,因为在构建 SQL 语句时,字符串仍然容易受到攻击发送到数据库。我相信你可以 google 如何做到这一点(因为我认为这是与你的问题完全不同的讨论)。
因此,您可以:
而不是新建一个新的 ReportParameterprivate ReportParameter GetReportParameter<T>(string name, T value)
{
return new ReportParameter
{
Name = name,
Value = value
};
}
然后在序列化的时候:
var xml = Serialize(
new ReportParameters
{
Parameters = new ReportParameter[]
{
GetReportParameter("Test", 4),
GetReportParameter("Startdate", new DateTime(2019, 04, 05, 22, 52, 25))
}
});
现在,您不再需要 class 类型的 ReportParameterOfInt32
和 ReportParameterOfDateTime
。
至于想要反序列化 XML,如果您打算存储任何报表设计器信息,您可能需要在 ReportParameter
中添加一个单独的 Type
字段。但是,我不确切知道您的要求是什么,但它可能会帮助您在运行时以更简单的方式确定 Value
属性 的 Type
。
更新
@Patrick B 发现可以使用 XmlArrayItem
指定 ReportParameter
的通用类型版本来完成此操作。有道理。
示例:
public class ReportParameters
{
[XmlArrayItem(Type = typeof(ReportParameter<int>))]
[XmlArrayItem(Type = typeof(ReportParameter<int?>))]
[XmlArrayItem(Type = typeof(ReportParameter<DateTime>))]
public List<ReportParameter> Parameters { get; set; } = new List<ReportParameter>();
}