将 XML 文件转换为可由 VCS 人工编辑和管理的文件

发布于 2024-09-08 00:23:31 字数 1076 浏览 4 评论 0原文

有时 XML 文件需要存储在某些 VCS 中。此类文件通常使用 GUI 工具进行编辑,这些工具可以根据需要每次对元素重新排序。

此外,VCS 合并通常是面向行的,并且通常 XML 文件要么看起来像一长行,要么像 那样完全缩进

<foo>
    <bar>
        <name>
            n3
        </name>
        <value>
            qqq3
        </value>
    </bar>
    <bar>
        <name>
            n2
        </name>
        <value>
            qqq2
        </value>
    </bar>
</foo>

,而它们应该看起来像

<foo>
    <bar>  <name> n2 </name>  <value> qqq2 </value> </bar>
    <bar>  <name> n3 </name>  <value> qqq3 </value> </bar>
</foo>

(例如“部分缩进”)以便更易于人类阅读/编辑、紧凑。一个简单的逻辑单元应该占据一行。

即使有人将 XML 文件转换为如此好的格式,其他人也会在 GUI 工具中对其进行编辑,该工具将重新排序并重新意图所有内容,这将是很糟糕的(不可读,VCS 将报告大量更改,尽管几乎没有实际更改)。

是否有现成的 XSLT 转换(或其他程序)将所有 XML 文件转换为某种统一格式(例如排序(如果元素顺序不重要)并统一空格)以及我可以在哪里指定哪些元素应该是 oneliners?

例如,如果我可以在 .gitattributes 中指定过滤器这样的转换,那么 git 会自动处理这个问题。

Sometimes XML files needs to be stored in some VCS. Such files are often edited using GUI tools which can reorder the elements each times as they want.

Also VCS merging is usually line-oriented, and often XML files either looks likes one long line or fully indented like

<foo>
    <bar>
        <name>
            n3
        </name>
        <value>
            qqq3
        </value>
    </bar>
    <bar>
        <name>
            n2
        </name>
        <value>
            qqq2
        </value>
    </bar>
</foo>

, while they should look like

<foo>
    <bar>  <name> n2 </name>  <value> qqq2 </value> </bar>
    <bar>  <name> n3 </name>  <value> qqq3 </value> </bar>
</foo>

(e.g. "partially indented") to be more human readable/editable, compact. One simple logical unit should occupy one line.

Even if somebody converts XML file to such nice format, someone else will edit it in GUI tool that will reorder and reintent everything and it will be bad (unreadable and VCS will report massive changes despite of there are almost no actual changes).

Is there ready made XSLT transformation (or other program) that converts all XML files to some unified format (e.g. sorts (if element order do not matter) and unifies whitespace) and where I can specify which elements should be oneliners?

For example, if I can specify such transformation as filter in .gitattributes and git will automatically handle this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

冷血 2024-09-15 00:23:31

我没有在每个 XSLT 处理器中进行测试(事实上,我只在 MSXSL 中进行了测试):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes"/>
    <xsl:template match="@*|node()" name="identity">
        <xsl:if test="self::bar">
            <xsl:text>
</xsl:text>
        </xsl:if>
        <xsl:copy>
            <xsl:apply-templates select="@*|node()">
            <xsl:sort select="normalize-space(name)"/>
            </xsl:apply-templates>
            <xsl:if test="self::foo">
                <xsl:text>
</xsl:text>
            </xsl:if>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="text()">
        <xsl:value-of select="normalize-space(.)"/>
    </xsl:template>
</xsl:stylesheet>

结果:

<foo>
<bar><name>n2</name><value>qqq2</value></bar>
<bar><name>n3</name><value>qqq3</value></bar>
</foo>

注意:XML 序列化可能会有所不同。如果是这种情况,请保留逻辑并序列化为 TEXT(您必须通过输出开始和结束标记以及属性来模拟 XML 序列化)

编辑:进行较小的更改,以便正确对不同的序列化输入进行排序。

I did not test in every XSLT processor (In fact, I only tested this in MSXSL):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes"/>
    <xsl:template match="@*|node()" name="identity">
        <xsl:if test="self::bar">
            <xsl:text>
</xsl:text>
        </xsl:if>
        <xsl:copy>
            <xsl:apply-templates select="@*|node()">
            <xsl:sort select="normalize-space(name)"/>
            </xsl:apply-templates>
            <xsl:if test="self::foo">
                <xsl:text>
</xsl:text>
            </xsl:if>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="text()">
        <xsl:value-of select="normalize-space(.)"/>
    </xsl:template>
</xsl:stylesheet>

Result:

<foo>
<bar><name>n2</name><value>qqq2</value></bar>
<bar><name>n3</name><value>qqq3</value></bar>
</foo>

Note: XML serialization may vary. If this is the case, preserve the logic and serialize as TEXT (You must simulate XML serialization by output opening and closing tags as well as attributes)

Edit: Minor change in order to properly sort diferent serializated input.

鸩远一方 2024-09-15 00:23:31

是的,有 XML 漂亮的打印机;我自己总是使用 xmllint

Yes, there are XML prettyprinters; I always use xmllint myself.

你げ笑在眉眼 2024-09-15 00:23:31

基于 http://www.dpawson.co 创建了我自己的排序缩进器。 uk/xsl/sect2/pretty.html 和 Alejandro 的答案:http://vi -server.org/vi/sortindent.xsl。镜像如下:

<!-- Change 'oneliner' to the name of element you want to see as one line -->
<!-- Remove 'xsl:sort' element if you don't want sorting -->
<!-- http://stackoverflow.com/questions/3157658/converting-xml-files-to-be-human-editable-and-managable-by-vcs/3160818#3160818 -->

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml"/>
   <xsl:param name="indent-increment" select="'   '" />

   <xsl:template match="*">
      <xsl:param name="skip_indent" select="name()='oneliner' or name()='another_oneliner'"/> 
      <xsl:param name="indent" select="'
'"/>

      <xsl:if test="not($skip_indent)">
      <xsl:value-of select="$indent"/>

      <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates>
        <xsl:with-param name="indent" select="concat($indent, $indent-increment)"/>
        <xsl:sort select="@*|node()"/> 
        </xsl:apply-templates>
        <xsl:if test="*">
          <xsl:value-of select="$indent"/>
        </xsl:if>

      </xsl:copy>

      </xsl:if>
      <xsl:if test="$skip_indent">
         <xsl:value-of select="$indent"/>
         <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates>
           <xsl:with-param name="indent" select="' '"/>
           <xsl:with-param name="skip_indent" select="1"/>
           <xsl:sort select="@*|node()"/> 
            </xsl:apply-templates>
         </xsl:copy>
      </xsl:if>
   </xsl:template>

   <xsl:template match="comment()|processing-instruction()">
      <xsl:copy />
   </xsl:template>

   <xsl:template match="text()">
       <xsl:param name="skip_indent" select="0"/> 
       <xsl:if test="$skip_indent">
           <xsl:value-of select="normalize-space(.)"/>
       </xsl:if>
       <xsl:if test="not($skip_indent)">
           <xsl:if test="not(normalize-space(.)='')">
              <xsl:value-of select="."/>
           </xsl:if>
       </xsl:if>
   </xsl:template>


</xsl:stylesheet>

现在,原始 XML 文件的“等效”转换(属性的重新排序除外)映射到相同的结果 XML,并且格式良好,并且我可以强制某些元素成为单行元素。

Created my own sort-indenter based on http://www.dpawson.co.uk/xsl/sect2/pretty.html and Alejandro's answer: http://vi-server.org/vi/sortindent.xsl. Mirrored here:

<!-- Change 'oneliner' to the name of element you want to see as one line -->
<!-- Remove 'xsl:sort' element if you don't want sorting -->
<!-- http://stackoverflow.com/questions/3157658/converting-xml-files-to-be-human-editable-and-managable-by-vcs/3160818#3160818 -->

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml"/>
   <xsl:param name="indent-increment" select="'   '" />

   <xsl:template match="*">
      <xsl:param name="skip_indent" select="name()='oneliner' or name()='another_oneliner'"/> 
      <xsl:param name="indent" select="'
'"/>

      <xsl:if test="not($skip_indent)">
      <xsl:value-of select="$indent"/>

      <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates>
        <xsl:with-param name="indent" select="concat($indent, $indent-increment)"/>
        <xsl:sort select="@*|node()"/> 
        </xsl:apply-templates>
        <xsl:if test="*">
          <xsl:value-of select="$indent"/>
        </xsl:if>

      </xsl:copy>

      </xsl:if>
      <xsl:if test="$skip_indent">
         <xsl:value-of select="$indent"/>
         <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates>
           <xsl:with-param name="indent" select="' '"/>
           <xsl:with-param name="skip_indent" select="1"/>
           <xsl:sort select="@*|node()"/> 
            </xsl:apply-templates>
         </xsl:copy>
      </xsl:if>
   </xsl:template>

   <xsl:template match="comment()|processing-instruction()">
      <xsl:copy />
   </xsl:template>

   <xsl:template match="text()">
       <xsl:param name="skip_indent" select="0"/> 
       <xsl:if test="$skip_indent">
           <xsl:value-of select="normalize-space(.)"/>
       </xsl:if>
       <xsl:if test="not($skip_indent)">
           <xsl:if test="not(normalize-space(.)='')">
              <xsl:value-of select="."/>
           </xsl:if>
       </xsl:if>
   </xsl:template>


</xsl:stylesheet>

Now "equivalent" transformations of the original XML file (except of reordering of attributes) maps to the same resulting XML, and it is fine formatted, and I can force some elements to be oneliners.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文