Java 如何在没有任何信息的情况下通过 JAXB 编组对象?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4146540/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 12:04:25  来源:igfitidea点击:

How to marshal an object via JAXB without any information about it?

javaxmljaxb

提问by yegor256

I have an object value, which is of some type, either @XmlRootElement-annotated, or not. I want to marshal it into XML:

我有一个 object value,它是某种类型的,要么带@XmlRootElement注释,要么不带注释。我想将其编组为 XML:

String value1 = "test";
assertEquals("<foo>test</foo>", toXml("foo", value1));
// ...
@XmlRootElement
class Bar {
  public String bar = "test";
}
assertEquals("<foo><bar>test</bar></foo>", toXml("foo", new Bar()));

Can I do it with JAXB existing facilities, or I should create some custom analyzer?

我可以使用 JAXB 现有设施来完成,还是应该创建一些自定义分析器?

采纳答案by bdoughan

You could leverage JAXBIntrospector to do the following:

您可以利用 JAXBIntrospector 执行以下操作:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBIntrospector;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;

public class Demo {


    public static void main(String[] args) throws Exception {
        Object value = "Hello World";
        //Object value = new Bar();

        JAXBContext jc = JAXBContext.newInstance(String.class, Bar.class);
        JAXBIntrospector introspector = jc.createJAXBIntrospector();
        Marshaller marshaller = jc.createMarshaller();
        if(null == introspector.getElementName(value)) {
            JAXBElement jaxbElement = new JAXBElement(new QName("ROOT"), Object.class, value);
            marshaller.marshal(jaxbElement, System.out);
        } else {
            marshaller.marshal(value, System.out);
        }
    }

    @XmlRootElement
    public static class Bar {

    }

}

With the above code when the JAXBElement is marshalled it will be qualified with an xsi:type attribute corresponding to the appropriate schema type:

使用上面的代码,当 JAXBElement 被编组时,它将使用对应于适当模式类型的 xsi:type 属性进行限定:

<ROOT 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">Hello World</ROOT>

To eliminate the qualification you can simply change the line that creates the JAXBElement to:

要消除限定,您只需将创建 JAXBElement 的行更改为:

JAXBElement jaxbElement = new JAXBElement(new QName("ROOT"), value.getClass(), value);

This will result in the following XML:

这将产生以下 XML:

<ROOT>Hello World</ROOT>

回答by skaffman

If it's not annotated with @XmlRootElement, then JAXB does not have sufficient information to marshal it. You would need to wrap it in a JAXBElementfirst.

如果它没有用 注释@XmlRootElement,那么 JAXB 没有足够的信息来封送它。你需要JAXBElement先把它包装起来。

Could you do some reflective lovin' to find out how to wrap the object in the appropriate JAXBElement?

你能不能做一些反思性的爱来找出如何用合适的包装对象JAXBElement

回答by Daniel Szalay

Here is how to marshal value1, which is a String. You can pass yourObject.getClass()to the JAXBElementconstructor, and value1:

下面是如何编组value1,这是一个String. 您可以传递yourObject.getClass()JAXBElement构造函数,并且value1

try {
    JAXBContext jc = JAXBContext.newInstance();
    Marshaller m = jc.createMarshaller();
    String value1 = "test";
    JAXBElement jx = new JAXBElement(new QName("foo"), value1.getClass(), value1);
    m.marshal(jx, System.out);
} catch (JAXBException ex) {
    ex.printStackTrace();
}

This works without using @XmlRootElement. The result of the code above was:

这在不使用@XmlRootElement. 上面代码的结果是:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo>test</foo>

On the other part, this will not work with a Barobject: javax.xml.bind.JAXBException: myPackage.Bar is not known to this context. However, you can get the value from inside Bar, and create the JAXBElementwith that, not the object itself.

另一方面,这不适用于Bar对象:javax.xml.bind.JAXBException: myPackage.Bar is not known to this context。但是,您可以从 inside 获取值Bar,并JAXBElement使用它创建,而不是对象本身。

回答by sajmons

I didnt find any good generic way of doing it. Here is my generic solution.

我没有找到任何好的通用方法。这是我的通用解决方案。

import javax.xml.bind.*;
import javax.xml.namespace.QName;
import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
import java.io.StringWriter;

public class XMLConverter {

    /**
     * Serialize object to XML string
     * @param object object
     * @param <T> type
     * @return
     */
    public static <T> String marshal(T object) {
        try {
            StringWriter stringWriter = new StringWriter();
            JAXBContext jc = JAXBContext.newInstance(object.getClass());
            Marshaller m = jc.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

            QName qName = new QName(object.getClass().getCanonicalName(), object.getClass().getSimpleName());
            JAXBElement<T> root = new JAXBElement(qName, object.getClass(), object);

            m.marshal(root, stringWriter);
            return stringWriter.toString();
        } catch (Exception e) {
            // log the exception
        }
        return null;
    }

    /**
     * Deserialize XML string back to object
     * @param content XML content
     * @param clasz class
     * @param <T> type
     * @return
     */
    public static <T> T unMarshal(final String content, final Class<T> clasz) {
        try {
            JAXBContext jc = JAXBContext.newInstance(clasz);
            Unmarshaller u = jc.createUnmarshaller();
            return u.unmarshal(new StreamSource(new StringReader(content)), clasz).getValue();
        } catch (Exception e) {
            // log the exception
        }
        return null;
    }

}