将XML名称空间声明移动到根元素

时间:2016-01-18 17:52:56

标签: java xml groovy xml-namespaces

我有一个XML字符串/对象:

<?xml version="1.0"?>
<ns0:top xmlns:ns0="someUrl1">
<ns1:child xmlns:ns1="someUrl2">
    Value
</ns1:child>
</ns0:top>

如何转换为&#34;标准化&#34;名称空间XML形式如下:

<?xml version="1.0"?>
<ns0:top xmlns:ns0="someUrl1" xmlns:ns1="someUrl2">
<ns1:child>
    Value
</ns1:child>
</ns0:top>

1 个答案:

答案 0 :(得分:0)

这可能是一个坏主意,原因有几个(很难涵盖很多边缘情况),但是(为了好玩和非常简单的情况,比如你提出的那个),这是一个使用SAXParser实现这一点的实现:

/* package whatever; // don't place package name! */

import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.util.*;
import java.lang.*;
import java.io.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone extends DefaultHandler
{
    StringBuilder sb;
    private Hashtable namespaces;
    private int nsIdx;
    boolean rootDone;
    String rootNodeName;
    String rootNS;

    public void startDocument() throws SAXException {
        namespaces = new Hashtable();
        nsIdx = 0;
        sb = new StringBuilder();
        rootDone  = false;
    }

    public void endDocument() throws SAXException {
        Set<String> keys = namespaces.keySet();
        StringBuilder rootEl = new StringBuilder();
        rootEl.append("<" + namespaces.get(rootNS) + ":" + rootNodeName);
            for(String key: keys){
                rootEl.append(" xmlns:" + namespaces.get(key) + "='" + key + "'");
            }
            rootEl.append(">");

            sb.insert(0, rootEl.toString());
            System.out.println(sb.toString());
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        for (int i = start; i < start + length; i++)
        {
            sb.append(ch[i]);
        }
    }    

    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {

        if (!namespaces.containsKey(namespaceURI))
        {
            nsIdx++;
            namespaces.put(namespaceURI, "ns" + nsIdx);
        }
        boolean printEl = rootDone;

        if (!rootDone)
        {
            rootNodeName = localName;
            rootNS = namespaceURI;
            rootDone = true;
        }
        else
        {
            sb.append("<" + namespaces.get(namespaceURI) + ":" + localName);
        }

        for (int i = 0; i < atts.getLength(); i++)
        {
            if (!namespaces.containsKey(atts.getURI(i)))
            {
                nsIdx++;
                namespaces.put(atts.getURI(i),"ns" + nsIdx);
            }
            if (atts.getLocalName(i) == atts.getQName(i))
            {
                sb.append(" " + atts.getLocalName(i) + "='" + atts.getValue(i) + "'");
            }
            else
            {
                sb.append(" " + namespaces.get(atts.getURI(i)) + ":" + atts.getLocalName(i) + "='" + atts.getValue(i) + "'");
            }
        }

        if (printEl)
        {
            sb.append(">");
        }
    }

    public void endElement(String uri, String localName, String qName) throws SAXException {
        sb.append("</" + namespaces.get(uri) + ":" + localName + ">");
    }

    public static void main (String[] args) throws java.lang.Exception
    {
        String xml = "<?xml version=\"1.0\"?><ns0:top xmlns:ns0=\"someUrl1\"><ns1:child xmlns:ns1=\"someUrl2\" ns0:testNS=\"tstNS\" test=\"tst\">Value</ns1:child><child2></child2><child3/></ns0:top>";
        SAXParserFactory spf = SAXParserFactory.newInstance();
        spf.setNamespaceAware(true);
        SAXParser saxParser = spf.newSAXParser();
        XMLReader xmlReader = saxParser.getXMLReader();
        xmlReader.setContentHandler(new Ideone());
        xmlReader.parse(new InputSource(new StringReader(xml)));

    }
}

(ideone:http://ideone.com/E4LIac

请注意,这将执行以下可能不合需要的事情:

  • 使自闭的节点为空(例如<child3 />变为<child3></child3>
  • 为未加前缀的节点创建前缀(例如xmlns:ns3=''
  • 可能没有正确处理其他一些边缘情况
  • 可能没有遵循最好的Java惯例,我有点生疏

另请注意,它只会将最终结果打印到System.out - 您可能希望将其返回或保存或其他内容。我不知道这是否或如何在groovy中工作。

相关问题