How to add an XML namespace (xmlns) when serializi

2020-08-23 02:38发布

问题:

I'm serializing Objects to XML with the help of XStream. How do I tell XStream to insert an xmlns to the XML output of my object?

As an example, I have this simple object I want to serialize:

@XStreamAlias(value="domain")
public class Domain
{
    @XStreamAsAttribute
    private String type;

    private String os;

    (...)
}

How do I achieve exactly the following output with XStream?

<domain type="kvm" xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0">
  <os>linux</os>
</domain>

回答1:

Alternatively, this use case could be handled quite easily with a JAXB implementation (Metro, EclipseLink MOXy, Apache JaxMe, etc):

Domain

package com.example;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Domain
{
    private String type;
    private String os;

    @XmlAttribute
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getOs() {
        return os;
    }

    public void setOs(String os) {
        this.os = os;
    }

}

package-info

@XmlSchema(xmlns={
        @XmlNs(
            prefix="qemu", 
            namespaceURI="http://libvirt.org/schemas/domain/qemu/1.0")
})
package com.example;

import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlSchema;

Demo

package com.example;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class Demo {

    public static void main(String[] args) throws JAXBException {
        JAXBContext jc = JAXBContext.newInstance(Domain.class);

        Domain domain = new Domain();
        domain.setType("kvm");
        domain.setOs("linux");

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(domain, System.out);
    }


}

Output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<domain xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0" type="kvm">
    <os>linux</os>
</domain>

For More Information

  • http://bdoughan.blogspot.com/2010/08/jaxb-namespaces.html
  • http://bdoughan.blogspot.com/2010/10/how-does-jaxb-compare-to-xstream.html


回答2:

XStream doesn't support namespaces but the StaxDriver it uses, does. You need to set the details of your namespace into a QNameMap and pass that into the StaxDriver:

QNameMap qmap = new QNameMap();
qmap.setDefaultNamespace("http://libvirt.org/schemas/domain/qemu/1.0");
qmap.setDefaultPrefix("qemu");
StaxDriver staxDriver = new StaxDriver(qmap);    
XStream xstream = new XStream(staxDriver);
xstream.autodetectAnnotations(true);
xstream.alias("domain", Domain.class);

Domain d = new Domain("kvm","linux");
String xml = xstream.toXML(d);

Output:

<qemu:domain type="kvm" xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0">
  <qemu:os>linux</qemu:os>
</qemu:domain>


回答3:

This is a bit of a hack, but it's quick and easy: add a field to your class called xmlns, and only have it non-null during serialisation. To continue your example:

@XStreamAlias(value="domain")
public class Domain
{
    @XStreamAsAttribute
    private String type;

    private String os;

    (...)

    @XStreamAsAttribute
    @XStreamAlias("xmlns:qemu")
    String xmlns;

    public void serialise(File path) {
        XStream xstream = new XStream(new DomDriver());
        xstream.processAnnotations(Domain.class);
        (...)

        PrintWriter out = new PrintWriter(new FileWriter(path.toFile()));
        xmlns = "http://libvirt.org/schemas/domain/qemu/1.0";
        xstream.toXML(this, out);
        xmlns = null;
    }
}

To be complete, setting xmlns = null should be in a finally clause. Using a PrintWriter also allows you to insert an XML declaration at the start of the output, if you like.