【问题标题】:How to marshal an object via JAXB without any information about it?如何在没有任何相关信息的情况下通过 JAXB 编组对象?
【发布时间】:2010-11-10 16:19:00
【问题描述】:

我有一个对象value,它属于某种类型,@XmlRootElement-annotated,或者没有。我想将其编组为 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()));

我可以使用 JAXB 现有的工具来做吗,或者我应该创建一些自定义分析器?

【问题讨论】:

    标签: java xml jaxb


    【解决方案1】:

    您可以利用 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 {
    
        }
    
    }
    

    使用上面的代码,当 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>
    

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

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

    这将产生以下 XML:

    <ROOT>Hello World</ROOT>
    

    【讨论】:

    • 完美运行!也许你知道如何摆脱xsi:type 属性?这就是我得到的 String 对象:&lt;phone xsi:type="xs:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;(239) 555 2390&lt;/phone&gt;
    • @Vincenzo 我已经更新了我的答案,详细说明了如何消除 xsi:type 属性。
    • @BlaiseDoughan 请看这个问题:stackoverflow.com/questions/26816798/…
    【解决方案2】:

    这是编组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();
    }
    

    这在不使用@XmlRootElement 的情况下有效。上面代码的结果是:

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

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

    【讨论】:

      【解决方案3】:

      如果它没有用@XmlRootElement 注释,那么 JAXB 没有足够的信息来编组它。您需要先将其包装在 JAXBElement 中。

      您能做一些反思性的爱好,以了解如何将对象包装在适当的JAXBElement 中吗?

      【讨论】:

      • 查看我的回答,了解如何使用 JAXBIntrospector 来避免任何反射调用。
      【解决方案4】:

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

      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;
          }
      
      }
      

      【讨论】:

      • 使用 object.getClass().getCanonicalName() 时命名空间错误
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-20
      • 2018-09-04
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多