代码之家  ›  专栏  ›  技术社区  ›  Vi.

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

  •  1
  • Vi.  · 技术社区  · 15 年前

    <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文件转换为某种统一格式(例如排序(如果元素顺序无关紧要)和统一空格),以及在哪里可以指定哪些元素应该是单行线?

    例如,如果我可以将这种转换指定为filter in .gitattributes git会自动处理这个问题。

    3 回复  |  直到 15 年前
        1
  •  1
  •   user357812 user357812    15 年前

    我没有在每个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>&#xA;</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>&#xA;</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序列化可能会有所不同。如果是这种情况,请保留逻辑并序列化为文本(必须通过输出打开和关闭标记以及属性来模拟XML序列化)

    :为了对不同的序列化输入正确排序而进行的微小更改。

        2
  •  0
  •   reinierpost    15 年前

    是的,有XML预打印机;我总是用 xmllint 我自己。

        3
  •  0
  •   Vi.    15 年前

    创建了我自己的排序压头基于 http://www.dpawson.co.uk/xsl/sect2/pretty.html 亚历杭德罗的回答是: 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="'&#xA;'"/>
    
          <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>