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

时间:2010-04-02 18:05:13

标签: xml pretty-print xml-formatting

我正在寻找可以进行XML漂亮打印的Windows的XSLT或命令行工具(或可以制作成命令行工具的C#代码等)。具体来说,我想要一个能够将属性一对一地放置的东西,例如:

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

它不一定非常像,但我想将它用于具有数十个属性的节点的XML文件,并将它们分布在多行中,使它们更易于阅读,编辑和文本差异。

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

7 个答案:

答案 0 :(得分:11)

这是一个小的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();

答案 1 :(得分:11)

这是一个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
     

你只需要这样做一次。

我希望这对你有用。

答案 2 :(得分:4)

在SourceForge上尝试Tidy。虽然它经常在[X] HTML上使用,但我之前在XML上成功使用过它 - 只需确保使用-xml选项。

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

  

Tidy读取HTML,XHTML和XML文件并写入清理过的标记。 ...对于通用XML文件,Tidy仅限于纠正基本的格式错误和漂亮的打印

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

Tidy有很多选项,包括:

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

  

缩进的属性
  顶部类型:布尔
  默认值:no示例:y / n,是/否,t / f,真/假,1/0   此选项指定Tidy是否应在新行上开始每个属性。

一个警告:

  

对XML的有限支持

     

符合W3C XML 1.0建议的XML处理器非常挑剔他们会接受哪些文件。 Tidy可以帮助您修复导致XML文件被拒绝的错误。 Tidy尚未识别所有XML功能,例如它不了解CDATA部分或DTD子集。

但我怀疑,除非你的XML真的先进,否则该工具应该可以正常工作。

答案 3 :(得分:2)

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

perl xmlpp.pl -t input.xml

您还可以通过创建名为attributeOrdering.txt的文件并调用perl xmlpp.pl -s -t input.xml来确定属性的顺序。有关更多选项,请使用perl xmlpp.pl -h

我希望,它没有太多的错误,但到目前为止它对我有用。

答案 4 :(得分:0)

XML Notepad 2007可以手动执行此操作...让我看看它是否可以编写脚本。

没有......它可以像这样启动它:

XmlNotepad.exe a.xml

其余的只是点击保存按钮。 Power Shell,其他工具可以实现自动化。

答案 5 :(得分:0)

只需使用此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

答案 6 :(得分:0)

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

<强> UPD

SAX代表Simple API for XML。它是XML解析的推送模型(Builder设计模式的经典示例)。 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))