15

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

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

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

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

4

7 回答 7

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

你只需要这样做一次。

我希望这对你有用。

于 2010-04-18T15:34:30.173 回答
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 ...with 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();
于 2010-04-18T09:46:12.197 回答
6

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

http://tidy.sourceforge.net/#docs

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

人们已经移植到几个平台,它可以作为一个可执行和可调用的库使用。

Tidy 有很多选择,包括:

http://api.html-tidy.org/tidy/quickref_5.0.0.html#indent

indent-attributes
顶部 类型:布尔
默认值:no 示例:y/n、yes/no、t/f、true/false、1/0
此选项指定 Tidy 是否应在新行中开始每个属性。

一个警告:

对 XML 的有限支持

符合 W3C 的 XML 1.0 推荐标准的 XML 处理器对于它们将接受哪些文件非常挑剔。Tidy 可以帮助您修复导致 XML 文件被拒绝的错误。不过,Tidy 还不能识别所有 XML 特性,例如它不理解 CDATA 部分或 DTD 子集。

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

于 2010-04-17T16:43:29.157 回答
2

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

perl xmlpp.pl -t input.xml

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

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

于 2010-04-12T18:00:29.987 回答
0

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

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

XmlNotepad.exe a.xml

剩下的就是点击保存按钮。Power Shell,其他工具可以自动化。

于 2010-04-02T18:15:25.610 回答
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

于 2010-04-02T18:18:10.047 回答
0

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

更新

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))
于 2010-04-12T19:57:28.210 回答