JAXB体验之旅第二篇:unmarshal

来源:互联网 发布:linux访问https地址 编辑:程序博客网 时间:2024/05/01 15:56
 今天研究了unmarshal,说白了其实就是xml文档读操作。其中遇到一个Unmarshaller.unmarshal方法返回实际对象的类型问题。起初个人一直觉得应该返回xml文档根元素类型的实例,通过实例操作发现不仅其然。
例子1:
xml schema:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="book">
   <xsd:complexType>
       <xsd:sequence>
       <xsd:element name="name" type="xsd:string"/>
      <xsd:element name="author" type="xsd:string"/>      
      <xsd:element name="ISBN" type="xsd:int"/>
      <xsd:element name="publisher" type="xsd:string"/>
      <xsd:element name="edition" type="xsd:int"/>
      <xsd:element type="xsd:double" name="price"/>
       </xsd:sequence>
    </xsd:complexType>
</xsd:element>
</xsd:schema>
对应xml 文档:
<?xml version="1.0" encoding="UTF-8"?>
<book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="src/simple.xsd">
  <name>hello china</name>
  <author>owen</author>
  <ISBN>1860</ISBN>
  <publisher>objectiva</publisher>
  <edition>1</edition>
  <price>10.0</price>
</book>
解析类:
       
        /*************************the code can make sure the root element of XML document automatically
        JAXBContext context = JAXBContext.newInstance("com.objectiva.JAXBTesting.book");

        Unmarshaller unMarshaller = context.createUnmarshaller();
        Object object = unMarshaller.unmarshal(new File("simple.xml"));
       
        if(object instanceof Book)
        {
        System.out.println(((Book)object).getName());
        System.out.println(((Book)object).getPublisher());
        System.out.println(((Book)object).getAuthor());
        System.out.println(((Book)object).getISBN());
        System.out.println(((Book)object).getEdition());
        }
       
        ******************************************************/
这个例子Unmarshalle.unmarshal方法返回的就是实际的xml文档根元素类型实例。
例子2:
xml schema:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="book" type="BookType"></xsd:element>
<xsd:complexType name="BookType">
   <xsd:sequence>
      <xsd:element name="name" type="xsd:string"/>
      <xsd:element name="author" type="xsd:string"/>      
      <xsd:element name="ISBN" type="xsd:int"/>
      <xsd:element name="publisher" type="xsd:string"/>
      <xsd:element name="edition" type="xsd:int"/>
      <xsd:element type="xsd:double" name="price"/>
    </xsd:sequence>
</xsd:complexType>
<xsd:element name="comment" type="xsd:string"/>
</xsd:schema>
对应xml 文档:
<?xml version="1.0" encoding="UTF-8"?>
<book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="simpleTest.xsd">
  <name>effective java</name>
  <author>ruly</author>
  <ISBN>10086</ISBN>
  <publisher>o'Reily</publisher>
  <edition>1</edition>
  <price>50.0</price>
</book>
解析类:
        /*************************the code can't make sure the root element of XML document automatically**/
        
        JAXBContext context = JAXBContext.newInstance("com.objectiva.JAXBTesting.book");
        Unmarshaller unMarshaller = context.createUnmarshaller();
        Object object = unMarshaller.unmarshal(new File("simpleTest.xml"));
       
        System.out.println(((JAXBElement<BookType>)object).getValue().getName());
       
        /*************************************************************/
这个例子解析后实际返回的是JAXBElement类型的一个实例。
分析后得出:在第一个例子中,JAXB框架可以根据xml schema确定出xml文档的根元素,因为在解析时可以直接返回根元素类型的实例。而在第二个例子中,JAXB框架无法确定xml文档的根元素,因此在解析时就只能返回JAXBElement类型的实例了。在第一个例子中,生成的java类中可以看到@XmlRootElement(name = "book") anonation,但是第二个例子就没有。这样正好印证了API所说:
Note that when the root element name is unknown and the root element has an @xsi:type, the XML data is unmarshalled using that JAXB mapped class as the value of a JAXBElement.

至此xml 文档的unmarshal操作就算结束了,xml文档的内容已经装载到java对象里程序可以直接操作了。

自己研究中参考了(http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html),对自己理解帮助颇大:
Why does JAXB put @XmlRootElement sometimes but not always?

isusanin asked in the JAXB forum:

When trying to marshall an object I get this error message:

unable to marshal type "org.blah.MessageType" as an element
because it is missing an @XmlRootElement annotation

If I add the @XmlRootElement annotation to the MessageType, it works,but my question is why doesn't the JAXB compiler put this annotation inautomatically. It's inconvenient because MessageType is automaticallygenerated and I don't want to edit it every time I regenerate the javaclasses.

Is there any better way to resolve this problem?

XJC does try to put @XmlRootElement annotation on a class that wegenerate from a complex type. The exact condition is somewhat ugly, butthe basic idea is that if we can statically guarantee that a complextype won't be used by multiple different tag names, we put@XmlRootElement. So for example, if the schema is as follows:

&lt;br&gt;&amp;amp;lt;schema&amp;amp;gt;&lt;br&gt;  &amp;amp;lt;element name="foo"&amp;amp;gt;&lt;br&gt;    &amp;amp;lt;complexType&amp;amp;gt;&lt;br&gt;      ...&lt;br&gt;    &amp;amp;lt;/complexType&amp;amp;gt;&lt;br&gt;  &amp;amp;lt;/element&amp;amp;gt;&lt;br&gt;&amp;amp;lt;/schema&amp;amp;gt;&lt;br&gt;

Then you'd get:

@XmlRootElement
class Foo {
...
}
But if the schema is as follows, then XJC can't eliminate thepossibility that your type might be used in other schemas (that can becompiled separately and put together at runtime):
&lt;br&gt;&amp;amp;lt;schema&amp;amp;gt;&lt;br&gt;  &amp;amp;lt;element name="foo" type="bar" /&amp;amp;gt;&lt;br&gt;  &amp;amp;lt;complexType name="bar" /&amp;amp;gt;&lt;br&gt;&amp;amp;lt;/schema&amp;amp;gt;&lt;br&gt;

So you'd get:

class Bar {
}
class ObjectFactory {
JAXBElement<Bar> createFoo(Bar value) { ... }
}

Now, the crucial part in the above inference done by XJC is that "your schema might be used by other schemas that XJC isn't compiling right now".This is certainly true for some users (and we learned that the hardway), but it's also true that for many users this is too conservativean assumption. It's often the case that what you are compiling isindeed the whole thing, and you want XJC to know that so that it canoptimize the generated code more aggressively.

Such notion isn't defined in the spec, but as an experiment wehave such aggressive optimization mode in XJC, tentatively called"simple-minded binding mode". Consider the following schema that usesthis mode (see that <xjc:simple/> customization):

&lt;br&gt;&amp;amp;lt;xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"&lt;br&gt;  xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"&lt;br&gt;  jaxb:version="1.0"&lt;br&gt;  xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"&lt;br&gt;  jaxb:extensionBindingPrefixes="xjc"&amp;amp;gt;&lt;br&gt;  &amp;amp;lt;xs:annotation&amp;amp;gt;&lt;br&gt;    &amp;amp;lt;xs:appinfo&amp;amp;gt;&lt;br&gt;      &amp;amp;lt;jaxb:globalBindings&amp;amp;gt;&lt;br&gt;        &amp;amp;lt;xjc:simple /&amp;amp;gt;&lt;br&gt;      &amp;amp;lt;/jaxb:globalBindings&amp;amp;gt;&lt;br&gt;    &amp;amp;lt;/xs:appinfo&amp;amp;gt;&lt;br&gt;  &amp;amp;lt;/xs:annotation&amp;amp;gt;&lt;br&gt;  &lt;br&gt;  &amp;amp;lt;xs:element name="foo" type="bar" /&amp;amp;gt;&lt;br&gt;  &amp;amp;lt;xs:complexType name="bar" /&amp;amp;gt;&lt;br&gt;&amp;amp;lt;/xs:schema&amp;amp;gt;&lt;br&gt;

From this you'll just get Foo class, since XJC now knows that only one element is using bar,and therefore there's no point in having two of them around (I justfixed a few issues wrt this, so if you want to try it, wait untiltomorrow and download nightly.) Also note that this is an experimental mode and it is subject to change.

Now, that said, there's another way to work around this issue. If all you want is just to marshal an object without @XmlRootElement, then you can just do:

marshaller.marshal( new JAXBElement(
new QName("uri","local"), MessageType.class, messageType ));
原创粉丝点击