Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAXB processing of XML sequences

Tags:

java

jaxb

I'm trying to process some XML files using the JAXB implementation shipped in Java 7. I'm using these versions :

501 ~ % xjc -version
xjc 2.2.4
502 ~ %java -version        
java version "1.7.0_01"
Java(TM) SE Runtime Environment (build 1.7.0_01-b08)
Java HotSpot(TM) Server VM (build 21.1-b02, mixed mode)

The problematic decalaration in the XML schema is the following :

<xsd:complexType name="CategorizeType">
    <xsd:complexContent>
        <xsd:extension base="se:FunctionType">
            <xsd:sequence>
                <xsd:element ref="se:LookupValue"/>
                <xsd:element ref="se:Value"/>
                <xsd:sequence minOccurs="0" maxOccurs="unbounded">
                    <xsd:element ref="se:Threshold"/>
                    <xsd:element ref="se:Value"/>
                </xsd:sequence>
                <xsd:element ref="se:Extension" minOccurs="0" />
            </xsd:sequence>
            <xsd:attribute name="thresholdBelongsTo"
                  type="se:ThresholdBelongsToType" use="optional"/>
        </xsd:extension>
    </xsd:complexContent>
</xsd:complexType>

As you can see, there are two explicit occurences of se:Value in the Type. However, it doesn't stop the compilation using xjc. And if I have a look in the Java class generated for this type, I can see that it is theoritically possible to retrieve the elements of

 <xsd:sequence minOccurs="0" maxOccurs="unbounded">
     <xsd:element ref="se:Threshold"/>
     <xsd:element ref="se:Value"/>
 </xsd:sequence>

using the following method :

public List<Object> getThresholdAndValue() {
    if (thresholdAndValue == null) {
        thresholdAndValue = new ArrayList<Object>();
    }
    return this.thresholdAndValue;
}

Unfortunately, if I try to get the elements of the list, i can only retrieve the elements registered as threshold in my xml file, where the CategorizeType instance is defined as follow :

 <Categorize thresholdsBelongTo="succeeding" fallbackValue="0">
     <LookupValue>
         <ns3:ValueReference>OUI_EEE92</ns3:ValueReference>
     </LookupValue>
     <Value>0.3</Value>
     <Threshold>30.0</Threshold>
     <Value>0.4</Value>
     <Threshold>40.0</Threshold>
     <Value>0.45</Value>
     <Threshold>45.0</Threshold>
     <Value>0.5</Value>
     <Threshold>50.0</Threshold>
     <Value>0.55</Value>
     <Threshold>55.0</Threshold>
     <Value>0.6</Value>
     <Threshold>60.0</Threshold>
     <Value>0.7</Value>
     <Threshold>70.0</Threshold>
     <Value>0.8</Value>
     <Extension>
         <ExtensionParameter name="method">MANUAL</ExtensionParameter>
     </Extension>
 </Categorize>

When retrieving the list, I can only see the Threshold values.

Do I make something wrong ? Is it an inner limitation of Jaxb ?

Note that I can't change the XML schema...

EDIT :

I've just run xjc with the -v option, and I obtain globally the same output. With verbosity :

xjc -verbose se/2.0/All.xsd
parsing a schema...
[WARNING] java.net.SocketException: Unexpected end of file from server
  line 23 of file:/home/alexis/crap/SE-Schema-2.0/ows/2.0/ows19115subset.xsd

[WARNING] java.net.SocketException: Unexpected end of file from server
  line 22 of file:/home/alexis/crap/SE-Schema-2.0/filter/2.0/filterCapabilities.xsd

compiling a schema...
[INFO] generating codee
unknown location

Without it :

xjc se/2.0/All.xsd 
parsing a schema...
[WARNING] java.net.SocketException: Unexpected end of file from server
  line 23 of file:/home/alexis/crap/SE-Schema-2.0/ows/2.0/ows19115subset.xsd

[WARNING] java.net.SocketException: Unexpected end of file from server
  line 22 of file:/home/alexis/crap/SE-Schema-2.0/ows/2.0/owsExceptionReport.xsd

compiling a schema...

The following output just contains the name and location of the generated files.

I have forgotten to say that this xsd couldn't be compiled with the xjc shipped with Java 6. Last tried has been made with JAXB 2.1.10.I can't reproduce this behaviour now, as I'm now working with Java 7.

EDIT2 :

I've just tried to customize the binginds, as suggested in comments. My binding file is the following :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jxb:bindings jxb:version="1.0"
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"    >
    <jxb:bindings schemaLocation="schema.xsd" 
                   node="//xsd:complexType[@name='CategorizeType']">
        <jxb:bindings 
                  node="xsd:complexContent/xsd:extension/xsd:sequence/xsd:element[@ref='se:Value'][position()=1]">
            <jxb:property name="FirstValue"/>
        </jxb:bindings>
    </jxb:bindings>
</jxb:bindings>

The first value instance is indeed replaced by a firstValue attribute in the Java code

@XmlElement(name = "Value", required = true)
protected ParameterValueType firstValue;
@XmlElements({
    @XmlElement(name = "Threshold", type = LiteralType.class),
    @XmlElement(name = "Value", type = ParameterValueType.class)
})
protected List<Object> thresholdAndValue;

public ParameterValueType getFirstValue() {
    return firstValue;
}
public void setFirstValue(ParameterValueType value) {
    this.firstValue = value;
}
public List<Object> getThresholdAndValue() {
    if (thresholdAndValue == null) {
        thresholdAndValue = new ArrayList<Object>();
    }
    return this.thresholdAndValue;
}

Unfortunately, I still obtain the same result - I still can't see my values in the list returned by getThresholdAndValues(). I'm still exploring the customization way...

like image 998
Agemen Avatar asked Nov 30 '11 12:11

Agemen


People also ask

What can I use instead of JAXB?

XOM, JDOM, dom4j, etc. etc. Projects like Castor and Apache XMLBeans predate JAXB, so you could have a look at those. Ulf Dittmer wrote: XOM, JDOM, dom4j, etc.

What is the advantage of JAXB?

JAXB simplifies access to an XML document from a Java program by presenting the XML document to the program in a Java format. The first step in this process is to bind the schema for the XML document into a set of Java classes that represents the schema.

Which component or tool in JAXB can generate Java files from a XML schema?

After the Java artifacts for your application are generated, you can generate fully annotated Java classes from an XML schema file by using the JAXB schema compiler, xjc command-line tool.


2 Answers

Update: Sorry I had to give up on this, I just couldn't get it to work either (it would be so much easier if you could change your schema!). I was using the Codehaus JAXB maven plugin. I'm pretty sure it's a conflict caused by the two Value elements, which I tried to fix with an xjb customisation:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jxb:bindings jxb:version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <jxb:bindings schemaLocation="../xsd/example.xsd" node="/xsd:schema">
        <jxb:schemaBindings>
            <jxb:package name="com.example" />
            <jxb:nameXmlTransform>
                <jxb:elementName suffix="Element"/>
            </jxb:nameXmlTransform>
        </jxb:schemaBindings>
    </jxb:bindings>
</jxb:bindings>

It had absolutely no effect. I tried <jxb:typeName suffix="Element"/>, and it renamed all of the JAXB classes, so I assume there's a bug with the jxb:elementName feature.

Original Post:

I've been looking at this and I'm able to replicate your problem.

I took a few liberties with your schema to simplify things, but it still contains the essential elements. Here is what I've been using:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:se="http://com.example/example" 
        targetNamespace="http://com.example/example"
    elementFormDefault="qualified">

    <xsd:element name="Categorize" type="se:CategorizeType" />

    <xsd:complexType name="FunctionType">
    </xsd:complexType>

    <xsd:simpleType name="Value">
        <xsd:restriction base="xsd:string"/>
    </xsd:simpleType>

    <xsd:simpleType name="Threshold">
        <xsd:restriction base="xsd:string"/>
    </xsd:simpleType>

    <xsd:complexType name="CategorizeType">
        <xsd:complexContent>
            <xsd:extension base="se:FunctionType">
                <xsd:sequence>
                    <xsd:element name="Value" type="se:Value" />
                    <xsd:sequence minOccurs="0" maxOccurs="unbounded">
                        <xsd:element name="Threshold" type="se:Threshold" />
                        <xsd:element name="Value" type="se:Value" />
                    </xsd:sequence>
                </xsd:sequence>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
</xsd:schema>

I've removed any elements/attributes not related to the issue, used name/type instead of ref, and defined the types missing from your provided schema.

My generated JAXB CategorizeType class looks like:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "CategorizeType", propOrder = {
    "value",
    "thresholdAndValue"
})
public class CategorizeType
    extends FunctionType
{

    @XmlElement(name = "Value", required = true)
    protected String value;
    @XmlElementRefs({
        @XmlElementRef(name = "Value", 
            namespace = "http://com.example/example", type = JAXBElement.class),
        @XmlElementRef(name = "Threshold", 
            namespace = "http://com.example/example", type = JAXBElement.class)
    })
    protected List<JAXBElement<String>> thresholdAndValue;

    ...
}

When I unmarshall your XML, my Categorize.Value is 0.8 (instead of the expected 0.3), and the values of each ThresholdAndValue (all Strings in my case) are 30.0, 40.0, 45.0, etc. The 0.4, 0.45, etc are missing.

When I remove the first Value element from the schema and then unmarshall, I get all of the expected values, so there's definitely a conflict!

However, when I marshall using the following JAXB setup:

ObjectFactory factory = new ObjectFactory();
CategorizeType catType = factory.createCategorizeType();
catType.setValue("0.3");
JAXBElement<String> thresh = factory.createCategorizeTypeThreshold("30.0");
JAXBElement<String> threshVal = factory.createCategorizeTypeValue("0.4");
JAXBElement<String> thresh2 = factory.createCategorizeTypeThreshold("40.0");
JAXBElement<String> threshVal2 = factory.createCategorizeTypeValue("0.45");
catType.getThresholdAndValue().add(thresh);
catType.getThresholdAndValue().add(threshVal);
catType.getThresholdAndValue().add(thresh2);
catType.getThresholdAndValue().add(threshVal2);
JAXBElement<CategorizeType> element = factory.createCategorize(catType);
// marshall to XML here!

I get the expected output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Categorize xmlns="http://com.example/example">
    <Value>0.3</Value>
    <Threshold>30.0</Threshold>
    <Value>0.4</Value>
    <Threshold>40.0</Threshold>
    <Value>0.45</Value>
</Categorize>

I'll keep looking into this!

like image 155
James Bassett Avatar answered Oct 08 '22 23:10

James Bassett


i think you need a complexType element around

            <xsd:sequence minOccurs="0" maxOccurs="unbounded">
                <xsd:element ref="se:Threshold"/>
                <xsd:element ref="se:Value"/>
            </xsd:sequence>

Can you insert an XSLT transformation to add a ComplexType to the schema definition before running xjc?

like image 23
BikeHikeJuno Avatar answered Oct 09 '22 00:10

BikeHikeJuno