代码之家  ›  专栏  ›  技术社区  ›  Scott Stafford

是否有可控制XML格式的样式表或Windows命令行工具,特别是每行放置一个属性?

  •  13
  • Scott Stafford  · 技术社区  · 14 年前

    我正在为Windows搜索一个XSLT或命令行工具(或可以生成命令行工具的C代码等),该工具可以进行XML打印。具体来说,我想要一个能够将属性一对一地放置的属性,比如:

    <Node>
       <ChildNode 
          value1='5'
          value2='6'
          value3='happy' />
    </Node>
    

    它不必完全是这样的,但我想将它用于具有几十个属性的节点的XML文件,并将它们分布在多行中,使它们更易于读取、编辑和文本差异。

    注意:我认为我的首选解决方案是一个XSLT工作表,我可以通过一个C方法,尽管Windows命令行工具也很好。

    7 回复  |  直到 6 年前
        1
  •  10
  •   Kirill Kobelev    7 年前

    这是一个小的C示例,它可以直接由您的代码使用,也可以内置到一个exe中,并在命令行调用为“ myexe from.xml to.xml “:

        using System.Xml;
    
        static void Main(string[] args)
        {
            XmlWriterSettings settings = new XmlWriterSettings {
                NewLineHandling = NewLineHandling.Entitize,
                NewLineOnAttributes = true, Indent = true, IndentChars = "  ",
                NewLineChars = Environment.NewLine
            };
    
            using (XmlReader reader = XmlReader.Create(args[0]))
            using (XmlWriter writer = XmlWriter.Create(args[1], settings)) {
                writer.WriteNode(reader, false);
                writer.Close();
            }
        }
    

    样本输入:

    <Node><ChildNode value1='5' value2='6' value3='happy' /></Node>
    

    示例输出(注意,可以删除 <?xml ... 具有 settings.OmitXmlDeclaration ):

    <?xml version="1.0" encoding="utf-8"?>
    <Node>
      <ChildNode
        value1="5"
        value2="6"
        value3="happy" />
    </Node>
    

    注意,如果你想要 一串 与其写文件,不如交换 StringBuilder 以下内容:

    StringBuilder sb = new StringBuilder();
    using (XmlReader reader = XmlReader.Create(new StringReader(oldXml)))
    using (XmlWriter writer = XmlWriter.Create(sb, settings)) {
        writer.WriteNode(reader, false);
        writer.Close();
    }
    string newXml = sb.ToString();
    
        2
  •  11
  •   Damian Powell    14 年前

    这是一个PowerShell脚本。它接受以下输入:

    <?xml version="1.0" encoding="utf-8"?>
    <Node>
        <ChildNode value1="5" value2="6" value3="happy" />
    </Node>
    

    …并将其作为输出:

    <?xml version="1.0" encoding="utf-8"?>
    <Node>
      <ChildNode
        value1="5"
        value2="6"
        value3="happy" />
    </Node>
    

    干得好:

    param(
        [string] $inputFile = $(throw "Please enter an input file name"),
        [string] $outputFile = $(throw "Please supply an output file name")
    )
    
    $data = [xml](Get-Content $inputFile)
    
    $xws = new-object System.Xml.XmlWriterSettings
    $xws.Indent = $true
    $xws.IndentChars = "  "
    $xws.NewLineOnAttributes = $true
    
    $data.Save([Xml.XmlWriter]::Create($outputFile, $xws))
    

    使用该脚本,将其保存为c:\formatxml.ps1。然后,从PowerShell提示键入以下内容:

    C:\formatxml.ps1 C:\Path\To\UglyFile.xml C:\Path\To\NeatAndTidyFile.xml
    

    这个脚本基本上只是使用.NET框架,所以您可以很容易地将它迁移到C应用程序中。

    注意:如果以前没有从PowerShell运行脚本,则必须在提升的PowerShell提示下执行以下命令,然后才能执行脚本:

    Set-ExecutionPolicy RemoteSigned
    

    不过,你只需要做一次。

    我希望这对你有用。

        3
  •  4
  •   Bert F    14 年前

    尝试 Tidy 在SourceForge上。虽然它经常在[x]HTML上使用,但我以前在XML上成功地使用过它-只要确保使用 -xml 选择权。

    http://tidy.sourceforge.net/docs/tidy_man.html

    Tidy读取HTML、XHTML和XML文件并写入清除的标记。…对于通用XML文件,整理仅限于纠正基本的格式良好性错误,以及 漂亮的印刷 .

    人们已经移植到多个平台,它作为可执行和可调用的库提供。

    Tidy有一堆选项,包括:

    http://tidy.sourceforge.net/docs/quickref.html#indent-attributes

    缩进属性
    顶部类型:布尔型
    默认值:否示例:是/否、是/否、T/F、真/假、1/0
    此选项指定整理是否应在新行上开始每个属性。

    一个警告:

    对XML的支持有限

    符合W3C的XML1.0建议的XML处理器对于它们将接受哪些文件非常挑剔。整理可以帮助您修复导致XML文件被拒绝的错误。尽管如此,Tidy还不能识别所有的XML特性,例如它不能理解CDATA部分或DTD子集。

    但我怀疑,除非您的XML是真正高级的,否则该工具应该可以正常工作。

        4
  •  2
  •   Chris Lercher    14 年前

    有一个工具,可以将属性拆分为每行一个: xmlpp . 这是一个Perl脚本,因此您必须安装 perl . 用途:

    perl xmlpp.pl -t input.xml
    

    您还可以通过创建名为attributeordering.txt的文件并调用 perl xmlpp.pl -s -t input.xml . 有关更多选项,请使用 perl xmlpp.pl -h

    我希望,它没有太多的bug,但到目前为止它已经对我有效了。

        5
  •  0
  •   Hamish Grubijan    14 年前

    XML记事本2007可以手动执行此操作…让我看看它是否可以被脚本化。

    不。。。它可以像这样启动:

    XmlNotepad.exe a.xml
    

    剩下的只是点击保存按钮。PowerShell,其他工具可以实现自动化。

        6
  •  0
  •   glebm    14 年前

    只需使用此XSLT:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="xml" encoding="ISO-8859-1"/>
      <xsl:param name="indent-increment" select="'   '"/>
    
      <xsl:template name="newline">
        <xsl:text disable-output-escaping="yes">
    </xsl:text>
      </xsl:template>
    
      <xsl:template match="comment() | processing-instruction()">
        <xsl:param name="indent" select="''"/>
        <xsl:call-template name="newline"/>    
        <xsl:value-of select="$indent"/>
        <xsl:copy />
      </xsl:template>
    
      <xsl:template match="text()">
        <xsl:param name="indent" select="''"/>
        <xsl:call-template name="newline"/>    
        <xsl:value-of select="$indent"/>
        <xsl:value-of select="normalize-space(.)"/>
      </xsl:template>
    
      <xsl:template match="text()[normalize-space(.)='']"/>
    
      <xsl:template match="*">
        <xsl:param name="indent" select="''"/>
        <xsl:call-template name="newline"/>    
        <xsl:value-of select="$indent"/>
          <xsl:choose>
           <xsl:when test="count(child::*) > 0">
            <xsl:copy>
             <xsl:copy-of select="@*"/>
             <xsl:apply-templates select="*|text()">
               <xsl:with-param name="indent" select="concat ($indent, $indent-increment)"/>
             </xsl:apply-templates>
             <xsl:call-template name="newline"/>
             <xsl:value-of select="$indent"/>
            </xsl:copy>
           </xsl:when>       
           <xsl:otherwise>
            <xsl:copy-of select="."/>
           </xsl:otherwise>
         </xsl:choose>
      </xsl:template>    
    </xsl:stylesheet>
    

    或者,作为另一个选项,这里是一个Perl脚本: http://software.decisionsoft.com/index.html

        7
  •  0
  •   phuclv    6 年前

    您可以实现一个简单的SAX应用程序,它将复制所有内容 as is 和缩进属性。

    UPD :

    SAX 代表 Simple API for XML . 它是XML解析的推送模型(构建器设计模式的经典示例)。API存在于大多数当前的开发平台中(尽管本机.NET类库缺少一个,具有xmlreader intead)

    这里是Python中的一个原始实现,它相当神秘,但是您可以实现主要的思想。

    from sys import stdout
    from xml.sax import parse
    from xml.sax.handler import ContentHandler
    from xml.sax.saxutils import escape
    
    class MyHandler(ContentHandler):
    
        def __init__(self, file_, encoding):
            self.level = 0
            self.elem_indent = '    '
    
            # should the next block make a line break
            self._allow_N = False
            # whether the opening tag was closed with > (to allow />)
            self._tag_open = False
    
            self._file = file_
            self._encoding = encoding
    
        def _write(self, string_):
            self._file.write(string_.encode(self._encoding))
    
        def startElement(self, name, attrs):
            if self._tag_open:
                self._write('>')
                self._tag_open = False
    
            if self._allow_N:
                self._write('\n')
                indent = self.elem_indent * self.level
            else:
                indent = ''
            self._write('%s<%s' % (indent, name))
    
            # attr indent equals to the element indent plus '  '
            attr_indent = self.elem_indent * self.level + '  '
            for name in attrs.getNames():
                # write indented attribute one per line
                self._write('\n%s%s="%s"' % (attr_indent, name, escape(attrs.getValue(name))))
    
            self._tag_open = True
    
            self.level += 1
            self._allow_N = True
    
        def endElement(self, name):
            self.level -= 1
            if self._tag_open:
                self._write(' />')
                self._tag_open = False
                return
    
            if self._allow_N:
                self._write('\n')
                indent = self.elem_indent * self.level
            else:
                indent = ''
            self._write('%s</%s>' % (indent, name))
            self._allow_N = True
    
        def characters(self, content):
            if self._tag_open:
                self._write('>')
                self._tag_open = False
    
            if content.strip():
                self._allow_N = False
                self._write(escape(content))
            else:
                self._allow_N = True
    
    
    if __name__ == '__main__':
        parser = parse('test.xsl', MyHandler(stdout, stdout.encoding))