2012-04-20 12 views
22

我有两个代码,在两个不同的java项目中,做几乎相同的事情,(根据xsd文件解组输入web服务)。JAXB unmarshaller.unmarshal何时返回JAXBElement <MySchemaObject>或MySchemaObject?

但在一种情况下,我应该这样写:(输入是一个占位符名)(元素的OMElement输入)

ClassLoader clInput = input.ObjectFactory.class.getClassLoader(); 
JAXBContext jc = JAXBContext.newInstance("input", clInput); 
Unmarshaller unmarshaller = jc.createUnmarshaller(); 
Input input = (Input)unmarshaller.unmarshal(element.getXMLStreamReader()); 

,并在其他的lib我必须使用JAXBElement.getValue(),因为它是返回一个JAXBElement的,和一个简单的(输入)投干脆崩溃:

Input input = (Input)unmarshaller.unmarshal(element.getXMLStreamReader()).getValue(); 

你知道是什么导致了这种差异?

+0

我认为寻找XSD是有道理的,因为它取决于您是否解组为SimpleType或ComplexType。 – Phani 2012-04-20 10:01:14

回答

19

如果根元素唯一对应一个Java类,那么这个类的一个实例将被返回,如果将不会返回JAXBElement

如果要确保始终获得域对象的实例,则可以使用JAXBInstrospector。下面是一个例子。

演示

package forum10243679; 

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

public class Demo { 

    private static final String XML = "<root/>"; 

    public static void main(String[] args) throws Exception { 
     JAXBContext jc = JAXBContext.newInstance(Root.class); 
     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     JAXBIntrospector jaxbIntrospector = jc.createJAXBIntrospector(); 

     Object object = unmarshaller.unmarshal(new StringReader(XML)); 
     System.out.println(object.getClass()); 
     System.out.println(jaxbIntrospector.getValue(object).getClass()); 

     Object jaxbElement = unmarshaller.unmarshal(new StreamSource(new StringReader(XML)), Root.class); 
     System.out.println(jaxbElement.getClass()); 
     System.out.println(jaxbIntrospector.getValue(jaxbElement).getClass()); 
    } 

} 

输出

class forum10243679.Root 
class forum10243679.Root 
class javax.xml.bind.JAXBElement 
class forum10243679.Root 
+1

出于好奇,为什么你会将你的软件包命名为'forum10243679'? – Saintali 2012-08-21 11:05:25

+11

@Saintali - 我尝试为每个答案提供一个有效的代码示例。如果需要重新访问,包名可以帮助我找到代码示例。我使用前缀'forum'作为与公共论坛相对应的代码示例,例如Stack Overflow(我使用bug前缀来重新创建错误),并且该数字对应于可以在URL中找到的问题ID。 – 2012-08-21 13:52:25

1

您需要添加到您的JAXB生成的类正确@XMLRootElement - 它应该有命名空间:

@XmlRootElement(namespace="http://your.namespace.com/", name="yourRootElement") 

看一看相关的问题(有很多很好的建议):Class Cast Exception when trying to unmarshall xml?

+2

注意:名称空间也可以使用@ javax.xml.bind.annotation.XmlSchema包注释(通常在package-info.java中)指定 – Puce 2012-04-20 10:03:37

4

这取决于您的根元素类中是否存在XmlRootElement annotation

如果从XSD生成的JAXB类,则应用下面的规则:

  • 如果根元素的类型是一个匿名类型 - > XmlRootElement将注释附加到生成的类
  • 如果根元素的类型是顶级类型 - >从生成的类中省略XmlRootElement注释

因此,我经常为根元素选择匿名类型。

您可以使用自定义文件自定义此匿名类型的类名称。例如。创建一个像这样的bindings.xjc文件:

<jxb:bindings version="1.0" 
       xmlns:jxb="http://java.sun.com/xml/ns/jaxb" 
       xmlns:xs="http://www.w3.org/2001/XMLSchema"> 
    <jxb:bindings schemaLocation="yourXsd.xsd" node="/xs:schema"> 
     <jxb:bindings node="//xs:element[@name='yourRootElement']"> 
      <jxb:class name="YourRootElementType"/> 
     </jxb:bindings> 
    </jxb:bindings> 
</jxb:bindings> 
0

修改生成的java类,我不同意。 不允许所有可能的xsd格式,我不同意。

对你所有解释的一个链接感谢,这是我编写的代码,以便照顾这两种情况,使用注释内省。它适用于输出和输入,并且是(在我的口味)更通用:

public class JaxbWrapper { 

    private static boolean isXmlRootElement(Class classT){ 

     Annotation[] annotations = classT.getAnnotations(); 

     for(Annotation annotation : annotations){ 
      if(annotation instanceof XmlRootElement){ 
       return true; 
      } 
     }  

     return false; 
    } 

    public static Object unmarshall(Class classObjectFactory, Class classObject, XMLStreamReader xmlStreamReader){ 

     Package pack = classObjectFactory.getPackage(); 
     String strPackageName = pack.getName(); 

     Object returnObject = null; 

     try { 
      JAXBContext jc = JAXBContext.newInstance(strPackageName, classObjectFactory.getClassLoader()); 

      Unmarshaller unmarshaller = jc.createUnmarshaller(); 

      returnObject = unmarshaller.unmarshal(xmlStreamReader); 

      boolean bIsRootedElement = isXmlRootElement(classObject); 
      if(!bIsRootedElement) 
      { 
       JAXBElement jaxbElement = (JAXBElement) returnObject; 
       returnObject = jaxbElement.getValue();    
      } 
     } 
     catch (JAXBException e) { 
      /*...*/ 
     } 

     return returnObject; 
    } 

    private static void writeToXml(Class classObjectFactory, Object obj, XMLStreamWriter xmlStreamWriter){ 

     Package pack = classObjectFactory.getPackage(); 
     String strPackageName = pack.getName(); 

     try {  
      JAXBContext jc = JAXBContext.newInstance(strPackageName, classObjectFactory.getClassLoader()); 
      Marshaller marshaller = jc.createMarshaller(); 
      marshaller.marshal(obj, xmlStreamWriter); 
     } 
     catch(JAXBException e) { 
      /*...*/ 
     }  
    } 

    public static String marshall(Class classObjectFactory, Class classObject, Object obj){ 

     Object objectToMarshall = obj; 

     boolean bIsRootedElement = isXmlRootElement(classObject); 
     if(!bIsRootedElement) 
     { 
      Package pack = classObjectFactory.getPackage(); 
      String strPackageName = pack.getName(); 

      String strClassName = classObject.getName(); 

      QName qName = new QName(strPackageName, strClassName); 

      JAXBElement jaxbElement = new JAXBElement(qName, classObject, null, obj); 

      objectToMarshall = jaxbElement; 
     } 

     StringWriter sw = new StringWriter(); 
     XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance(); 
     XMLStreamWriter xmlStreamWriter = null; 

     try { 
      xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(sw); 

      writeToXml(classObjectFactory, objectToMarshall, xmlStreamWriter); 

      xmlStreamWriter.flush(); 
      xmlStreamWriter.close(); 
     } 
     catch (XMLStreamException e) { 
      /*...*/ 
     } 

     return sw.toString(); 
    } 
} 
+1

您应该能够利用'JAXBIntrospector'类来简化代码。我添加了一个答案:http://stackoverflow.com/a/10253282/383861 – 2012-04-20 20:46:41

0

我有同样的问题。 JAXB unmarshaller.unmarshal返回JAXBElement<MyObject>而不是所需的MyObject。我发现并删除了@XmlElementDecl。问题已经解决了。