如何解决使用 XML 和 DTD 的 XML 到对象
<?xml version="1.0"?>
<!DOCTYPE report SYstem "01.dtd" [
<!ENTITY parameter "blablabla">
]>
<report xmlns="http://tempuri.org/report"
details="Something is described ¶meter;"
></report>
我试图将这个 XML 解析为一个对象,但在 details 属性中反序列化后,我得到了这个结果:“有些东西被描述为 ¶meter;”
但我想得到这个结果:“有些东西被描述为blablabla”。
我的代码如下:
class Program
{
static void Main(string[] args)
{
readxmlwithDTD();
}
public static void readxmlwithDTD()
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.XmlResolver = new XmlUrlResolver();
settings.ValidationType = ValidationType.DTD;
settings.DtdProcessing = DtdProcessing.Parse;
settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);
settings.IgnoreWhitespace = true;
var files = Directory.GetFiles("../../../App_data/include/","01.xml",SearchOption.AllDirectories);
foreach (var file in files)
{
XmlDocument xmlDoc = new XmlDocument();
using (StringReader sr = new StringReader(file))
using (XmlReader reader = XmlReader.Create(sr,settings))
{
xmlDoc.Load(file);
}
report r = DeserializetoObject<report>(xmlDoc.OuterXml);
}
Console.ReadLine();
}
public static T DeserializetoObject<T>(string xml) where T : class
{
System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(T));
MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
return (T)ser.Deserialize(memStream);
}
private static void ValidationCallBack(object sender,ValidationEventArgs e)
{
if (e.Severity == XmlSeverityType.Warning)
Console.WriteLine("Warning: Matching schema not found. No validation occurred." + e.Message);
else // Error
Console.WriteLine("Validation error: " + e.Message);
}
}
我应该改变什么?
解决方法
无需将您的 XML 加载到中间 XmlDocument
中。您可以在反序列化期间通过 XmlSerializer
扩展实体,只要您向序列化程序传递一个配置了 DtdProcessing.Parse
的 XmlReader
。
即如果我将您的反序列化代码概括如下:
public static partial class XmlSerializationHelper
{
public static T LoadFromXmlWithDTD<T>(string filename,XmlSerializer serial = default,ValidationEventHandler validationCallBack = default)
{
var settings = new XmlReaderSettings
{
// This will throw an exception if uncommented:
// System.Xml.XmlException: An error has occurred while opening external DTD 'file:///app/01.dtd': Could not find file '/app/01.dtd'
// XmlResolver = new XmlUrlResolver(),DtdProcessing = DtdProcessing.Parse,IgnoreWhitespace = true,};
settings.ValidationEventHandler += validationCallBack;
serial = serial ?? new XmlSerializer(typeof(T));
using (var reader = XmlReader.Create(filename,settings))
return (T)serial.Deserialize(reader);
}
}
你可以这样称呼它:
var report = XmlSerializationHelper.LoadFromXmlWithDTD<report>(filename,validationCallBack: ValidationCallBack);
并且 Details
将正确展开:
Assert.AreEqual("Something is described blablabla",report.Details);
注意事项:
-
您可能想要设置
XmlReaderSettings.MaxCharactersFromEntities
:此属性允许您减轻拒绝服务攻击,在这种攻击中,攻击者提交试图通过扩展实体超出内存限制的 XML 文档。通过限制扩展实体产生的字符,您可以检测攻击并可靠地恢复。
-
在以下代码中:
using (StringReader sr = new StringReader(file)) using (XmlReader reader = XmlReader.Create(sr,settings)) { xmlDoc.Load(file); }
您创建了一个
XmlReader
,它使用StringReader
来解析文件名file
就好像它是一个 XML 字符串而不是文件名字符串 --然后您忽略您创建的阅读器并使用xmlDoc.Load(file);
直接按名称加载文件内容。这似乎忽略了您刚刚构建的settings
,并且可能是您的错误的直接原因。 -
如果未找到指定的外部 DTD 文件(未包含在您的问题中),则取消注释
XmlResolver = new XmlUrlResolver()
将导致抛出异常Could not find file '/app/01.dtd'
。
演示小提琴here。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。