我的最终目标是存储一些用于生成用于报告的SQL的参数,以尝试减少(或删除?)SQL注入的可能性。
因此,在我的解决方案中,我计划在应用程序的管理面板中创建报告定义。报表定义是用于生成输出的原始SQL,然后有一个参数定义要在UI中呈现给用户,并尝试保持强类型化。
参数是我当前卡住的位置。我可以正确地序列化参数列表,并且XML看起来像我期望的那样,但是在某些例外情况下尝试反序列化结果。
我试图构建一个简单的方法来将XML字符串转换为对象并获得一些收益。我了解异常和原因,只是不知道如何解决。
当我尝试反序列化通用对象时,它将失败。在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(或类似参数)
这就是我要序列化/反序列化我所谈论的内容的地方。
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;
}
}
}
然后我有一个扩展方法来序列化数据(最终将其写入表中)
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;
}
}
}
编写测试方法进行简单的反序列化后,我得到一个可爱的异常:
消息:测试方法X.Tests.TestSerialize.SerializeGeneric抛出异常: System.InvalidOperationException:XML文档(1,170)中存在错误。 ---> System.InvalidOperationException:无法识别指定的类型:name ='ReportParameterOfInt32',namespace =“,at。
这里的任何帮助将不胜感激。
答案 0 :(得分:1)
不幸的是,我为您提供了解决方案,但与您的期望不符。我不知道您是否尝试过此方法,但我希望这至少会对您有所帮助。
如果我错了,请纠正我,但是除非您推出自己的反序列化器,否则似乎无法为您要完成的工作提供支持。
可能缺少的是一种通知Xml序列化程序使用通用类ReportParameter<T>
以及应解释的T
类型的方法。
因此,我没有采用通用参数方法,而是明确地手动滚动了这些类。
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”要求的Generic类型。但您仍然会遇到字符串的问题。 您需要确保转义任何字符,这将使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))
}
});
现在,您不再需要ReportParameterOfInt32
和ReportParameterOfDateTime
的不同类类型。
对于要反序列化XML,如果要存储任何报表设计器信息,则可能需要在Type
中添加一个单独的ReportParameter
字段。但是,我不清楚您的要求是什么,但是它可能会在运行时帮助您以更简单的方式确定Type
属性的Value
。
更新
@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>();
}