XML 到对象使用 XML 和 DTD
Posted
技术标签:
【中文标题】XML 到对象使用 XML 和 DTD【英文标题】:XML to object using XML and DTD 【发布时间】:2021-05-13 12:06:09 【问题描述】:我有一个 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 解析为一个对象,但在详细信息属性中反序列化后,我得到以下结果:“Something is describe ¶meter;”
但我想得到这个结果:“Something is describe 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);
我应该改变什么?
【问题讨论】:
LINQ to XML 会自动为您展开实体引用,请参阅dotnetfiddle.net/TrcgqT。是你想要的吗?从您的问题中甚至不清楚为什么要加载到中间XmlDocument
然后反序列化它的 OuterXml。
【参考方案1】:
无需将您的 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.
【讨论】:
以上是关于XML 到对象使用 XML 和 DTD的主要内容,如果未能解决你的问题,请参考以下文章
Hibernate -- xxx.hbm.xml ORM元数据 表对象关系映射文件
从零开始的Java开发2-10-1 XML入门:XML文档语法规则DTD与XML SchemaDom4j与XPath表达式