【问题标题】:XML to object using XML and DTDXML 到对象使用 XML 和 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 &parameter;"
></report>

我试图将此 XML 解析为一个对象,但在详细信息属性中反序列化后,我得到以下结果:“Something is describe &parameter;”

但我想得到这个结果:“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。

标签: c# .net xml dtd xmlserializer


【解决方案1】:

无需将您的 XML 加载到中间 XmlDocument 中。您可以在反序列化期间通过 XmlSerializer 扩展实体,只要您将配置为 DtdProcessing.ParseXmlReader 传递给序列化程序即可。

即如果我将您的反序列化代码概括如下:

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.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-08
    • 1970-01-01
    • 2023-03-31
    • 2016-09-18
    • 2014-09-28
    • 2014-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多