复制包含文档类型、实体和表示法的 XML 文件 XSLT 3

Copying an XML file including doctype, entities and notations XSLT 3

提问人:Caroline 提问时间:9/13/2022 最后编辑:Caroline 更新时间:3/2/2023 访问量:147

问:

我有一系列 XML 文档从一个文件夹复制到另一个文件夹,使用 msxsl.exe 1.1.0.1 和 XSLT 1.0 样式表进行转换,然后复制回原始文件夹。我不知道为什么没有复制文档类型、实体和符号,但目前它们正在样式表中使用 javascript 插入。我必须用 XSLT 3.0 替换 javascript,以便它可以与 saxon HE11 一起使用。

doctype 是 XML 中最高的元素,这也是我想要的输出:

  <!DOCTYPE dmodule [
  <!ENTITY ICN-XXX12-001-01 SYSTEM "ICN-XXX12-001-01.SWF" NDATA swf >
  <!ENTITY ICN-XXX49-001-01 SYSTEM "ICN-XXX49-001-01 SYSTEM.CGM" NDATA cgm >
  <!ENTITY ICN-AAA235-000000-0-A-001-01 SYSTEM "ICN-AAA235-000000-0-A-001-01.wrlzip" NDATA WRLZIP>
  <!NOTATION cgm PUBLIC "-//USA-DOD//NOTATION Computer Graphics Metafile//EN" >
  <!NOTATION swf PUBLIC "-//S1000D//NOTATION X-SHOCKWAVE-FLASH 3D Models Encoding//EN" >
  <!NOTATION WRLZIP SYSTEM "WRLZIP">
]>
<dmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:dc="http://www.purl.org/dc/elements/1.1/"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns:xlink="http://www.w3.org/1999/xlink"
         xsi:noNamespaceSchemaLocation="../schema/proced.xsd">
         <content>
            <figure>
              <title/>
                <graphic infoEntityIdent="ICN-XXX49-001-01"/>
            </figure>
            <proceduralStep>
                <para>Check the brake system function.</para>
                <multimedia>
                   <title>Brake function</title>
                   <multimediaObject autoPlay="1" fullScreen="0" infoEntityIdent="ICN-XXX12-001-01" multimediaType="other"/>
                </multimedia>
             </proceduralStep>
             <multimedia>
                 <multimediaObject infoEntityIdent="ICN-AAA235-000000-0-A-001-01"
                        multimediaType="3D"
                        xlink:href="ICN-AAA235-000000-0-A-001-01.wrlzip"
                        xlink:type="simple"/>
                </multimedia>
         </content>
</dmodule>

实体是从各种元素引用的,但并不总是指示文件类型:@infoEntityIdent

    <graphic infoEntityIdent="ICN-XXX49-001-01"/>
    <multimediaObject autoPlay="1" fullScreen="0" infoEntityIdent="ICN-XXX12-001-01"
                                           multimediaType="other"/>
    <multimediaObject infoEntityIdent="ICN-AAA235-000000-0-A-001-01"
                        multimediaType="3D" xlink:href="ICN-AAA235-000000-0-A-001-01.wrlzip"
 xlink:type="simple"/>

我可以正确插入文档类型,但我不知道如何访问实体和符号:

<xsl:template match="/">
    <xsl:text>&#xA;</xsl:text>
    <xsl:text disable-output-escaping="yes">&lt;!DOCTYPE </xsl:text>
    <xsl:value-of select="local-name(child::*)"/>
    <xsl:text> [</xsl:text> 
    <!-- entities and notations here -->  
    <xsl:text disable-output-escaping="yes">]&gt;</xsl:text>
    <xsl:text>&#xA;</xsl:text>
    <xsl:copy> 
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>

电流输出:

<!DOCTYPE dmodule []>
<dmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:dc="http://www.purl.org/dc/elements/1.1/"
     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     xsi:noNamespaceSchemaLocation="../schema/proced.xsd">
     <content>
        <figure>
          <title/>
            <graphic infoEntityIdent="ICN-XXX49-001-01"/>
        </figure>
        <proceduralStep>
            <para>Check the brake system function.</para>
            <multimedia>
               <title>Brake function</title>
               <multimediaObject autoPlay="1" fullScreen="0" infoEntityIdent="ICN-XXX12-001-01" multimediaType="other"/>
            </multimedia>
         </proceduralStep>
         <multimedia>
           <multimediaObject infoEntityIdent="ICN-AAA235-000000-0-A-001-01"
                    multimediaType="3D"
                    xlink:href="ICN-AAA235-000000-0-A-001-01.wrlzip"
                    xlink:type="simple"/>
            </multimedia>
     </content>

这是样式表中继承的 javascript,它确实给出了所需的结果:

<msxsl:script language="JavaScript" implements-prefix="js">

    <![CDATA[
function doctype(root) {
    var fso = new ActiveXObject('Scripting.FileSystemObject');
    var basepath = unescape(
        root
            .item(0)
            .url
            .replace(/^file:\/{3,}/, '')
            .replace(/^file:/, '')
            .replace(/[^\/]+$/, '')
            .replace(/\//g, '\\')
    );
    var entities = [];
    var notations = [];
    var needSVGNotations = false;
    if (root.item(0).doctype) {
        entities = root.item(0).doctype.entities;
        notations = root.item(0).doctype.notations;
    }
    var syntax = '\n<!DOCTYPE ' + root.item(0).documentElement.nodeName + ' [\n';
    for (var i = 0; i < entities.length; i++) {
        var entity = entities.item(i);

            var s = entity.xml;
            syntax += s + '\n';
    }
    for (var i = 0; i < notations.length; i++) {
        var s = notations.item(i).xml;
        syntax += s + '\n';
    }
    syntax += ']>\n';
    return syntax;
}
    ]]>
  </msxsl:script>

这是使用 javascript 的模板:

<xsl:template match="/">
    <xsl:value-of select="js:doctype(.)" disable-output-escaping="yes"/>
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
XML 文档类型 xslt-3.0 xml-entities

评论

1赞 Martin Honnen 9/13/2022
DTD/Doctypes 不是 XSLT/XPath(和 XQuery)数据模型的一部分,实际上没有任何版本。目前尚不清楚您的尝试是否适用于 XSLT 1.0 和 msxsl(以及这是否真的是使用的 W3C XSLT 1.0 语言,而不是 MSXSL 3 及更早版本也支持的专有 WD(工作草案)版本)。因此,如果您澄清这一点会有所帮助。在 XSLT 1、2 或 3 中没有复制 DOCTYPE 的内置方法,您需要依靠专有扩展来读取和/或输出此类详细信息。
1赞 Martin Honnen 9/13/2022
就像 Saxon 的许多东西一样,如果你离开官方规范,商业版本中通常会有一个扩展,即 PE 和 EE 有 saxonica.com/html/documentation11/extensions/instructions/...和 saxonica.com/html/documentation11/extensions/instructions/...,例如。我不知道这是否适合您,无论如何,它只是为了以更干净的方式输出细节,然后必须完全依赖母鹿。
1赞 Martin Honnen 9/13/2022
读取有关DTD的详细信息是有限的,尽管例如 saxonica.com/html/documentation11/functions/fn/...saxonica.com/html/documentation11/functions/fn/...
0赞 Martin Honnen 9/13/2022
如果您使用的是 Java 版本的 Saxon,您还可以查看 LexEv andrewjwelch.com/lexev 预处理器,它应该有助于保留“输出中的 DOCTYPE”。但我不确定最新的撒克逊版本是否很好地支持它,作者似乎继续在线使用它,希望它继续有效。
0赞 Caroline 9/13/2022
谢谢,马丁,我会调查你提供的链接。javascript 有效,我正在使用 msxsl.exe 1.1.0.1。我会看看我是否可以获得 PE 或 EE。

答:

1赞 Michael Kay 9/13/2022 #1

DTD/DOCTYPE 中的信息不是 XSLT 数据模型的一部分,因此它不会从 XML 分析器传递到 XSLT 处理器,这意味着您不能在纯 XSLT 中执行此操作。

Andrew Welch 有一个名为 LEXEV 的实用程序,它预处理 XML 文档以创建元素和属性的 DTD 表示形式,然后您可以使用 XSLT 以正常方式(或保持不变)对其进行转换。最后进行后处理回 DTD 语法。我已经很多年没有使用它了,但我希望它仍然有效。

评论

0赞 Martin Honnen 9/13/2022
我今天早上尝试了从 andrewjwelch.com/lexev 下载的 LexEv.jar同时使用 Java 8 和 Java 11 以及 Saxon 11 HE 但无法通过简单地在 Saxon 命令行上设置来使其工作,我收到错误“转换失败,配置问题:net.sf.saxon.trans.XPathException:无法实例化类 com.andrewjwelch.lexev.LexEv”。从 Java 应用程序运行它,我得到了更好的错误诊断,即正在寻找 Apache Xerces(),一旦我添加了 Apache Xerces 的 jar,它就开始工作了。-x:com.andrewjwelch.lexev.LexEvorg.apache.xerces.parsers.SAXParser
1赞 Caroline 10/12/2022 #2

Martin Honnen 的建议是一切,因为它返回了所有需要的信息。他还调整了我的代码,使其更加简洁,并更正了我最初在 .unparsed-entity-uri()analyze-string

  <xsl:template match="/">
    <xsl:call-template name="getDocType"/>
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template name="getDocType">
    <xsl:text>&#xA;</xsl:text>
    <xsl:text disable-output-escaping="yes">&lt;!DOCTYPE </xsl:text>
    <!-- doctype will either be pm or dmodule -->
    <xsl:value-of select="local-name(child::*)"/>
    <xsl:text> [</xsl:text>
    <!-- get a list of all @infoEntityIdent(s). Declare as attribute()* for unparsed-entity-uri() -->
    <xsl:variable name="infoEntityIdent" as="attribute()*" select="(descendant::symbol | descendant::barCode | descendant::multimedia/multimediaObject | descendant::graphic)/@infoEntityIdent"/>
    <xsl:text>&#xA;</xsl:text>
    
    <!-- write out the entity declaration -->
    <xsl:for-each select="$infoEntityIdent">
      <xsl:text disable-output-escaping="yes">&lt;!ENTITY </xsl:text>
      <xsl:value-of select="."/>
      <xsl:text> SYSTEM "</xsl:text>
      <xsl:variable name="uri" as="xs:anyURI" select="unparsed-entity-uri(.)"/>
      <!-- remove everything before, and including, the Original directory, leaving any graphics directory -->      
      <xsl:value-of select="replace($uri,'^.*Original/(.*)$','$1')"/>
      <xsl:text>" NDATA </xsl:text>
      <!-- print out the extension -->
      <xsl:value-of select="replace($uri, '.*?([^.]+)$', '$1')"/>
      <!-- close the declaration -->
      <xsl:text disable-output-escaping="yes">&gt;</xsl:text>
      <xsl:text>&#xA;</xsl:text>
    </xsl:for-each>
    
    <!-- get a list of notations -->
    <xsl:variable name="notations" select="$infoEntityIdent ! unparsed-entity-uri(.) ! replace(., '.*?([^.]+)$', '$1')"/>
    <xsl:for-each select="distinct-values($notations)">
      <xsl:choose>
        <xsl:when test="matches(.,'JPE?G','i')">
          <xsl:text disable-output-escaping="yes" expand-text="1">&lt;!NOTATION {.} PUBLIC "+//ISBN 0-7923-9432-1::Graphic Notation//NOTATION Joint Photographic Experts Group Raster//EN"&gt;</xsl:text>
        </xsl:when>
        <xsl:when test="matches(.,'cgm', 'i')">
          <xsl:text disable-output-escaping="yes" expand-text="1">&lt;!NOTATION {.} PUBLIC "-//USA-DOD//NOTATION Computer Graphics Metafile//EN"&gt;</xsl:text>
        </xsl:when>
        <xsl:when test="matches(.,'wrlzip', 'i')">
          <xsl:text disable-output-escaping="yes" expand-text="1">&lt;!NOTATION {.} SYSTEM "WRLZIP"&gt;</xsl:text>
        </xsl:when>
        <xsl:when test="matches(.,'svg', 'i')">
          <xsl:text disable-output-escaping="yes" expand-text="1">&lt;!NOTATION {.} SYSTEM "SVG"&gt;</xsl:text>
        </xsl:when>
        <xsl:when test="matches(.,'tiff', 'i')">
          <xsl:text disable-output-escaping="yes" expand-text="1">&lt;!NOTATION {.} SYSTEM "TIFF"&gt;</xsl:text>
        </xsl:when>
        <xsl:when test="matches(.,'png', 'i')">
          <xsl:text disable-output-escaping="yes" expand-text="1">&lt;!NOTATION {.} PUBLIC "-//W3C//NOTATION Portable Network Graphics//EN"&gt;</xsl:text>
        </xsl:when>
        <xsl:when test="matches(.,'swf', 'i')">
          <xsl:text disable-output-escaping="yes" expand-text="1">&lt;!NOTATION {.} PUBLIC "-//S1000D//NOTATION X-SHOCKWAVE-FLASH 3D Models Encoding//EN"&gt;</xsl:text>
        </xsl:when>
      </xsl:choose>
      <xsl:text>&#xA;</xsl:text>
    </xsl:for-each>    
    <xsl:text disable-output-escaping="yes">]&gt;</xsl:text>
    <xsl:text>&#xA;</xsl:text>
    <xsl:text>&#xA;</xsl:text>
  </xsl:template>