简体   繁体   中英

Take a DOM and re-order nodes and attributes in alphabetical order

I'd like to read in an XML document, re-order the nodes and attributes in a deterministic order, eg:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com</groupId>
    <artifactId>foobar</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</project>

Becomes:

<project>
    <artifactId>foobar</artifactId>
    <groupId>com</groupId>
    <modelVersion>4.0.0</modelVersion>
    <version>0.0.1-SNAPSHOT</version>
</project>

I'd also like to do attributes too, and indentation!

Your example doesn't have any attributes, and generally you can't control order of attributes. But sorting elements in XSLT is easy:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:template match="*">
  <xsl:copy>
    <xsl:apply-templates>
      <xsl:sort select="name()"/>
    </xsl:apply-templates>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

您可以使用 XSLT 转换 XML 数据并使用<xsl:sort>对元素进行排序

I can recommend JDOM2 for this kind of work. Your proposal can be achieved like this:

import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.filter.Filters;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;

public void transformXmlFile(File oldFile, File newFile) throws Exception
{
    Document oldDocument = new SAXBuilder().build(oldFile);
    Document newDocument = new Document(transformElement(oldDocument.getRootElement()));

    List<Element> children = new LinkedList<Element>();

    for (Element oldElement : oldDocument.getRootElement().getChildren())
        children.add(transformElement(oldElement));

    for (Element oldElement : sortElements(children))
        newDocument.getRootElement().addContent(oldElement);

    XMLOutputter serializer = new XMLOutputter(Format.getPrettyFormat());
    serializer.output(newDocument, new FileOutputStream(newFile));
}

private Element transformElement(Element oldElement)
{
    Element newElement = new Element(oldElement.getName(), oldElement.getNamespace());

    List<Attribute> attributes = new LinkedList<Attribute>();

    for (Attribute a: oldElement.getAttributes())
        attributes.add(a.clone());

    for (Attribute a: sortAttributes(attributes))
        newElement.getAttributes().add(a);

    return newElement;
}

private List<Attribute> sortAttributes(List<Attribute> attributes)
{
    Collections.sort(attributes, new Comparator<Attribute>()
    {
        @Override
        public int compare(Attribute a1, Attribute a2)
        {
            return a1.getName().compareTo(a2.getName());
        }
    });

    return attributes;
}


private List<Element> sortElements(List<Element> elements)
{
    Collections.sort(elements, new Comparator<Element>()
    {
        @Override
        public int compare(Element e1, Element e2)
        {
            return e1.getName().compareTo(e2.getName());
        }
    });

    return elements;
}

This is really mkdev's answer, but tweaked to sort the entire file rather than just the top level node.

import java.io.File;
import java.io.FileOutputStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

import org.jdom2.Attribute;
import org.jdom2.Content;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

//Source: https://stackoverflow.com/a/17502935/1361991
public class XMLSorter2 {
    public void transformXmlFile(File oldFile, File newFile) throws Exception
    {
        Document oldDocument = new SAXBuilder().build(oldFile);
        Document newDocument = new Document((Element) transformElement(oldDocument.getRootElement()));

        List<Content> children = new LinkedList<Content>();

        for (Content oldElement : oldDocument.getRootElement().getContent())
            children.add(transformElement(oldElement));

        for (Content oldElement : sortElements(children))
            newDocument.getRootElement().addContent(oldElement);

        XMLOutputter serializer = new XMLOutputter(Format.getPrettyFormat());
        serializer.output(newDocument, new FileOutputStream(newFile));
    }

    private Content transformElement(Content oldContent)
    {
        if(!(oldContent instanceof Element))
            return oldContent.clone();
        Element oldElement = (Element)oldContent;
        
        Element newElement = new Element(oldElement.getName(), oldElement.getNamespace());

        List<Attribute> attributes = new LinkedList<Attribute>();

        for (Attribute a: oldElement.getAttributes())
            attributes.add(a.clone());

        for (Attribute a: sortAttributes(attributes))
            newElement.getAttributes().add(a);

        List<Content> children = new LinkedList<Content>();

        for (Content oldElementChildren : oldElement.getContent())
            children.add(transformElement(oldElementChildren));

        for (Content oldElementChildren : sortElements(children))
            newElement.addContent(oldElementChildren);

        return newElement;
    }

    private List<Attribute> sortAttributes(List<Attribute> attributes)
    {
        Collections.sort(attributes, new Comparator<Attribute>()
        {
            public int compare(Attribute a1, Attribute a2)
            {
                return a1.getName().compareTo(a2.getName());
            }
        });

        return attributes;
    }


    private List<Content> sortElements(List<Content> elements)
    {
        Collections.sort(elements, new Comparator<Content>()
        {
            public int compare(Content e1, Content e2)
            {
                if(e1 instanceof Element && e2 instanceof Element)
                    return ((Element)e1).getName().compareTo(((Element)e2).getName());
                else if(e1 instanceof Element)
                    return -1;
                else if(e2 instanceof Element)
                    return 1;
                else
                    return e1.getValue().compareTo(e2.getValue());
            }
        });

        return elements;
    }
    
    public static void main(String[] args) {
        try {
            new XMLSorter2().transformXmlFile(new File(args[0]), new File(args[1]));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM