Delphi 11.3中FormatXMLData格式化SVG报无效节点类型异常的解决方法
修复Delphi 11.3中FormatXMLData处理SVG XML时的"Invalid node type"异常
问题背景
我有一个由Affinity Designer导出的SVG文件:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 67 67" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><g id="icon"><path d="M64.583,8.333c0,-3.449 -2.8,-6.25 -6.25,-6.25l-50,0c-3.449,0 -6.25,2.801 -6.25,6.25l0,50c0,3.45 2.801,6.25 6.25,6.25l50,0c3.45,0 6.25,-2.8 6.25,-6.25l0,-50Z" style="fill:#ebebeb;fill-opacity:0;stroke:#0dcaf2;stroke-width:4.01px;"/><g><circle cx="46.657" cy="16.816" r="8.809" style="fill:#f3ca69;"/><path d="M59.761,54.246l-15.758,-21.165c-0.508,-0.683 -1.548,-0.681 -2.054,0.003l-7.002,9.485l-11.004,-14.732c-0.506,-0.679 -1.525,-0.679 -2.032,-0l-14.753,19.758c-0.163,0.219 -0.253,0.485 -0.253,0.758l0,10.307l52.856,0l0,-4.414Z" style="fill:#4ea17e;fill-rule:nonzero;"/></g></g></svg>
在Delphi 11.3中,使用以下代码加载该文件并尝试用FormatXMLData格式化时触发Invalid node type异常(异常类型为EXMLDocError):
var ThisXMLDoc := Xml.XMLDoc.TXmlDocument.Create(nil); try ThisXMLDoc.LoadFromFile(EditSVGPath.Text); var FormattedSVGText := Xml.XMLDoc.FormatXMLData(ThisXMLDoc.XML.Text); // 触发异常 finally ThisXMLDoc.Free; end;
错误细节更新:异常发生在Xml.XMLDoc.pas中的CloneNodeToDoc函数,该函数仅允许处理节点类型为ntElement、ntAttribute、ntText、ntCData、ntComment、ntEntityRef、ntProcessingInstr或ntDocFragment的节点,而FormatXMLData传入的节点类型不在此范围内。
修复方案
原因分析
SVG文件中的DOCTYPE声明被解析为ntDocumentType类型节点,而Delphi原生的FormatXMLData函数不支持处理该类型节点,导致校验失败抛出异常。
具体解决方法
方法1:加载XML时忽略DTD声明
通过添加doNotLoadDTD选项,让XML解析器跳过DOCTYPE节点的加载,避免触发不支持的节点类型校验:
var ThisXMLDoc := Xml.XMLDoc.TXmlDocument.Create(nil); try ThisXMLDoc.Options := ThisXMLDoc.Options + [doNotLoadDTD]; ThisXMLDoc.LoadFromFile(EditSVGPath.Text); var FormattedSVGText := Xml.XMLDoc.FormatXMLData(ThisXMLDoc.XML.Text); finally ThisXMLDoc.Free; end;
方法2:预处理文本移除DOCTYPE后再格式化
如果需要保留XML声明但可舍弃DOCTYPE,可先读取文件文本,移除DOCTYPE部分后再格式化:
var RawSVGText: string; FormattedSVGText: string; begin // 读取原始SVG文件内容 RawSVGText := TFile.ReadAllText(EditSVGPath.Text); // 移除DOCTYPE声明 RawSVGText := TRegEx.Replace(RawSVGText, '<!DOCTYPE[^>]+>', '', [roIgnoreCase]); // 执行格式化 FormattedSVGText := Xml.XMLDoc.FormatXMLData(RawSVGText); end;
若业务需要保留DOCTYPE,可在格式化后手动将其添加回XML头部。
方法3:仅格式化根节点XML内容
直接获取SVG根节点的XML内容进行格式化,避开文档级的DOCTYPE节点,之后可手动补回XML声明:
var ThisXMLDoc: Xml.XMLDoc.TXmlDocument; RootNode: IXMLNode; FormattedSVGText: string; begin ThisXMLDoc := Xml.XMLDoc.TXmlDocument.Create(nil); try ThisXMLDoc.LoadFromFile(EditSVGPath.Text); RootNode := ThisXMLDoc.DocumentElement; // 格式化根节点的XML FormattedSVGText := Xml.XMLDoc.FormatXMLData(RootNode.XML); // 手动添加XML声明(按需) FormattedSVGText := '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' + sLineBreak + FormattedSVGText; finally ThisXMLDoc.Free; end;
内容的提问来源于stack exchange,提问作者user1580348
相关产品推荐
相关产品推荐

