XML排序/格式化工具

28

有没有一种工具可以对XML文件进行漂亮的格式化,并将其元素和属性排序?


重复:https://dev59.com/AGDVa4cB1Zd3GeqPaBsb... 但这里的答案对我更有用。 - CAD bloke
5个回答

14

我在寻找一种类似的工具,但并没有找到我想要的,所以我自己写了一个。它非常简单(不包括节点排序中的属性),但很有效。

也许对其他人有用.. 它在 GitHub 上。

这是 GitHub 页面中的一部分...

USAGE: sortxml.exe [options] infile [outfile]

  infile      The name of the file to sort, etc.
  outfile     The name of the file to save the output to.
              If this is omitted, then the output is written to stdout.

OPTIONS:

  --pretty    Ignores the input formatting and makes the output look nice.
  --sort      Sort both the nodes and attributes.
  --sortnode  Sort the nodes.
  --sortattr  Sort the attributes.

(prefix an option with ! to turn it off.)

默认情况下,输出漂亮且排序的节点和属性。以下是一个示例:

> type sample.xml
<?xml version="1.0" encoding="utf-8" ?><root><node value="one" attr="name"/></root>

> sortxml.exe sample.xml
<?xml version="1.0" encoding="utf-8"?>
<root>
  <node attr="name" value="one" />
</root>

14

我喜欢这个工具:https://xmlsorter.codeplex.com/

你可以按标签名称和属性进行排序。在比较一些XML文件之前,我喜欢使用它。

XML Sorter主窗口


这甚至无法加载XML文件。出现了“无效的XML”错误。无法处理doctype标记。一旦删除它,它就可以工作了。 - ScrappyDev
这对我来说是有效的,用于测试一对具有超过7K行且具有半复杂xml结构的文件,因此我最初留下了深刻印象。没有任何错误。下载、启动和执行都非常顺畅/快速。值得注意的一件事是它允许“排序属性”和“排序特定属性”,然后您可以选择您想要的属性。您可以检查两个选项,它们似乎重叠。 - Kent Kruckeberg
这是关于编程的内容:"Sort attributes" 和 "Sort by specific attributes" 是非常不同的。第一个是为每一行排序属性,而第二个是按特定属性的内容对所有行进行排序。我认为后者非常实用。 - Gertsen
2
看起来 CodePlex 将很快关闭,该页面仍在 WebArchive 上,但项目存档未链接到页面,因此这是 WebArchive 中项目存档的链接 https://web.archive.org/web/20210309105113/https://codeplexarchive.blob.core.windows.net/archive/projects/XmlSorter/XmlSorter.zip。 - Sambuccid
Codeplex现在肯定是关闭了,之前提供的评论中的链接仍然有效。 - Patric
Codeplex现在肯定是关闭了,之前提供的评论中的链接仍然有效。 - undefined

7

由于 Visual Studio 经常重新排序和重写 EDMX 文件(Entity Framework),让人感到沮丧(也可以参见此Uservoice),因此我编写了一些 Linqpad 代码来重新排序。然而,它很容易(并且很明显)在 LinqPad 之外使用。

它通过元素类型(标签)对元素进行排序,然后按元素属性“Name”的值进行排序,最后通过一些其他东西尝试使其具有确定性(不同的 XML,但是相同的含义通常会产生相同的输出 - 请参见代码)。

它还对属性进行排序。请注意,从语义上讲,XML 属性可能没有(相关的)顺序,但是从文本上讲,它们确实有顺序,并且版本控制系统仍然将它们视为纯文本...

(请注意,它不会修复在团队之间重新生成 Entity Framework edmx 文件时出现的不同别名问题

void Main()
{
    XDocument xdoc = XDocument.Load(@"\\filepath1\file1.edmx");

    var orderedElements = CopyAndSortElements(xdoc.Elements());

    var newDoc = new XDocument();
    newDoc.Add(orderedElements);
    newDoc.Save(@"\\filepath1\file1.Ordered.edmx");
}

public IEnumerable<XElement> CopyAndSortElements(IEnumerable<XElement> elements)
{
    var newElements = new List<XElement>();
    // Sort XElements by Tag & name-attribute (and some other properties)
    var orderedElements = elements.OrderBy(elem => elem.Name.LocalName) // element-tag
                                  .ThenByDescending(elem => elem.Attributes("Name").Count()) // can be 0, more than 1 is invalid XML
                                  .ThenBy(elem => (elem.Attributes("Name").Any() ? elem.Attributes("Name").First().Value.ToString() : string.Empty))
                                   // in case of no Name-Attributes, try to sort by (number of) children
                                  .ThenBy(elem => elem.Elements().Count())
                                  .ThenBy(elem => elem.Attributes().Count())
                                  // next line may vary for textually different but semantically equal input when elem & attr were unordered on input, but I need to restrain myself...
                                  .ThenBy(elem => elem.ToString());
    foreach (var oldElement in orderedElements)
    {
        var newElement = new XElement(oldElement.Name);
        if (oldElement.HasElements == false && string.IsNullOrEmpty(oldElement.Value) == false)
        {
            // (EDMX does not have textual nodes, but SO-users may use it for other XML-types ;-) )
            // IsNullOrEmpty-check: not setting empty value keeps empty-element tag, setting value (even empty) causes start-tag immediately followed by an end-tag
            // (empty-element tags may be a matter of taste, but for textual comparison it will matter!)
            newElement.Value = oldElement.Value;
        }
        var orderedAttrs = oldElement.Attributes().OrderBy(attr => attr.Name.LocalName).ThenBy(attr => attr.Value.ToString());
        newElement.Add(orderedAttrs);
        newElement.Add(CopyAndSortElements(oldElement.Elements()));
        newElements.Add(newElement);
    }
    return newElements;
}

PS:最终我们采用了一位同事同时编写的XSLT。我认为它更容易地/更好地适合每个人的构建过程。 但希望这对某些人有所帮助。


你最终使用了哪个XSLT? - Steven T. Cramer
哦,看这个 - 它会从像<thing> value </thing>的节点中剥离值。 - CAD bloke
1
@CADbloke,我添加了一些用于节点文本值的代码。 - Yahoo Serious
@StevenT.Cramer,抱歉,我刚刚才看到你的问题,而且我不记得了。 - Yahoo Serious

5
我发现了这篇文章:http://www.biglist.com/lists/xsl-list/archives/200106/msg01225.html,它使用以下XSLT来缩进XML并排序属性:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="*">
    <xsl:copy>
      <!-- Sort the attributes by name. -->
      <xsl:for-each select="@*">
        <xsl:sort select="name( . )"/>
        <xsl:copy/>
      </xsl:for-each>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

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

</xsl:stylesheet>

我还没有尝试过,但很有可能我会坚持使用XSLT来进行格式化。


这很适合对XML属性进行排序,那么我如何对标签的子元素进行排序呢? - Natim
完美的解决方案。它按字母顺序排序所有属性。谢谢! - Sauer

0

在尝试对 edmx 文件进行排序时,我遇到了这篇文章。我的解决方案基于 Arvo Bowens 的解决方案,该方案可以在以下链接中找到https://dev59.com/BXA75IYBdhLWcg3wGlIa#19324438

void Main()
{
    XDocument xdoc = XDocument.Load(@"C:\git\Nvision\Source\NvisionEntities\NvisionModel.edmx");
    Sort(xdoc.Root);
    xdoc.Save(@"C:\git\Nvision\Source\NvisionEntities\NvisionModel.edmx");
}

public void Sort(XElement source, bool bSortAttributes = true)
{
    //Make sure there is a valid source
    if (source == null) throw new ArgumentNullException("source");

    //Sort attributes if needed
    if (bSortAttributes)
    {
        List<XAttribute> sortedAttributes = source.Attributes().OrderBy(a => a.ToString()).ToList();
        sortedAttributes.ForEach(a => a.Remove());
        sortedAttributes.ForEach(a => source.Add(a));
    }

    //Sort the children IF any exist
    List<XElement> sortedChildren = source.Elements().OrderBy(elem => elem.Attributes("Name").Any() ? elem.Attributes("Name").First().Value.ToString() : string.Empty).ToList();
    if (source.HasElements)
    {
        source.RemoveNodes();
        sortedChildren.ForEach(c => Sort(c));
        sortedChildren.ForEach(c => source.Add(c));
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接