We have just have some xsd tutorials, now we use xsd we had and convert it to Java class to use.
If you are not familiar with JAXB or similar framework (In my experience, I have written some ORM related code of EJB, like Hibernate, so I can understand how this might be like somewhat easily), you may want to skim oracle document first.
Map Inheritance Relation
We have already have the inheritance in xsd to reuse type, and we need to translate it Java code.
<xs:complexType name="parentType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="childType">
<xs:complexContent>
<xs:restriction base="parentType">
<xs:sequence>
<xs:element name="name" type="specialStr"/>
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
This blog post gives the direction, I use the substitution group and mark father type as element reference like blog code does:
@XmlRootElement
public class Customer {
private ContactInfo contactInfo;
@XmlElementRef
public ContactInfo getContactInfo() {
return contactInfo;
}
public void setContactInfo(ContactInfo contactInfo) {
this.contactInfo = contactInfo;
}
}
But It give the following error:
Invalid @XmlElementRef : Type "class" or any of its subclasses are not known to this context
I search the internet, and found the answer in this SO question. Because the lazy loading of sub class, it is impossible for JAXB to use right sub class at right time. So we need make it known by @XmlSeeAlso
(make sub class loaded) and @XmlElements
(mapping name to sub class).
// to let jaxb find your subclass
@XmlSeeAlso({Address.class, PhoneNumber.class})
public class ContactInfo {
}
// to map your element to specific class
@XmlElements({
@XmlElement(name="address", type=Address.class),
@XmlElement(name="phoneNumber", type=PhoneNumber.class)
})
public List<ContactInfo> getContactInfo() {
return contactInfo;
}
Map ‘Class and Object` Relation XML Elements
In xml, I have a type and some types extend from it by using restriction
to restrict it attribute range (refer my related blog if you not familiar with restriction
).
<xs:complexType name="A">
<xs:attribute name="p" type="xs:float" default="0.5"/>
</xs:complexType>
<xs:complexType name="B">
<xs:complexContent>
<xs:restriction base="A">
<xs:attribute name="p" fixed="1" use="prohibited"/>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
In my cases, this is more like to init some objects of a type rather than the classical inheritance in Java. So I have only one class in Java, and want to map multiple types to a single class.
Again, @XmlElements
works like magic:
@XmlElements({
@XmlElement(name = "B", type = B.class),
@XmlElement(name = "C", type = C.class),
@XmlElement(name = "A", type = A.class)
})
public List<A> getA() {
return a;
}
Map ID and IDREF
This is a problem cause us a lot of time, you can skim the second part if you just care about problem and solution, not the way to solve it.
Problem
I have following simplified input xml:
<A id="1">
...
... (many levels of nested element)
<C>
<B ref="2"/>
</C>
...
</A>
<A id="2">...</A>
So I define the following xsd (simplified also):
<xs:complexType name="A" abstract="true">
...
<xs:element name="C" type="C">
...
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
<xs:complexType name="C">
<xs:choice>
<xs:element name="B" type="B"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="B">
<xs:attribute name="ref" type="xs:IDREF" use="required"/>
</xs:complexType>
And using the following classes:
public class A {
private String id;
@XmlAttribute
@XmlID
public String getId() {
return id;
}
...
}
public class C {
private A b;
@XmlIDREF
@XmlElement
public A getB() {
return b;
}
}
But when I try to map it, I met the error message with:
Undefined ID “”.
My problem is posted on SO, but no one answer me. So I have to investigate it by myself.
Roadmap to Solution
The problem is either lay in java code or in xsd definition, so I check them one by one.
I first view some examples of how to use id and idref in xsd, but not found any error in my definition.
Then I try to verify whether my java code is right. I view some code samples on SO (searching questions related with jaxb & IDREF
) and in this blog whose writer is the implementer of one implementation of JAXB.
Still not found similar error or code in my scenario, I decide to try some experiments. I code the java class in idea, then I output corresponding input xml to try whether my code is right. It works like magic.
This makes me wonder my xsd file. So I thought If I can output the class’s corresponding xsd, I could make some comparison to find my error! Searching the internet, I found this answer.
By comparing generated xsd file and my xsd definition, I found that generated xsd is following:
<xs:complexType name="C">
<xs:choice>
<xs:element name="B" type="xs:IDREF"/>
</xs:choice>
</xs:complexType>
<!-- and my definition is following:-->
<xs:complexType name="C">
<xs:choice>
<xs:element name="B" type="B"/>
</xs:choice>
</xs:complexType>
We suddenly understand it. What C
holds is not directly a ref, but a type has ref.
Solution
Up to this point, we know the source of error, and solution is simple now.
Either to change the xsd to:
<xs:complexType name="C">
<xs:choice>
<xs:element name="B" type="xs:IDREF"/>
</xs:choice>
</xs:complexType>
Or to change the java code:
public class C {
private B b;
...
}
// addint a B class which contains the reference
public class B {
private A b;
@XmlIDREF
@XmlElement
public A getB() {
return b;
}
}
Can’t find your problem? You may refer to the second part of this tutorial for more problems.
Ref
Written with StackEdit.
评论
发表评论