跳至主要内容

JAXB Problem List (1)

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.

评论

此博客中的热门博文

Spring Boot: Customize Environment

Spring Boot: Customize Environment Environment variable is a very commonly used feature in daily programming: used in init script used in startup configuration used by logging etc In Spring Boot, all environment variables are a part of properties in Spring context and managed by Environment abstraction. Because Spring Boot can handle the parse of configuration files, when we want to implement a project which uses yml file as a separate config file, we choose the Spring Boot. The following is the problems we met when we implementing the parse of yml file and it is recorded for future reader. Bind to Class Property values can be injected directly into your beans using the @Value annotation, accessed via Spring’s Environment abstraction or bound to structured objects via @ConfigurationProperties. As the document says, there exists three ways to access properties in *.properties or *.yml : @Value : access single value Environment : can access multi...

Elasticsearch: Join and SubQuery

Elasticsearch: Join and SubQuery Tony was bothered by the recent change of search engine requirement: they want the functionality of SQL-like join in Elasticsearch! “They are crazy! How can they think like that. Didn’t they understand that Elasticsearch is kind-of NoSQL 1 in which every index should be independent and self-contained? In this way, every index can work independently and scale as they like without considering other indexes, so the performance can boost. Following this design principle, Elasticsearch has little related supports.” Tony thought, after listening their requirements. Leader notice tony’s unwillingness and said, “Maybe it is hard to do, but the requirement is reasonable. We need to search person by his friends, didn’t we? What’s more, the harder to implement, the more you can learn from it, right?” Tony thought leader’s word does make sense so he set out to do the related implementations Application-Side Join “The first implementation ...

Learn Spring Expression Language

When reading the source code of some Spring based projects, we can see some code like following: @Value( "${env}" ) private int value ; and like following: @Autowired public void configure (MovieFinder movieFinder, @ Value ("#{ systemProperties[ 'user.region' ] } ") String defaultLocale) { this.movieFinder = movieFinder; this.defaultLocale = defaultLocale; } In this way, we can inject values from different sources very conveniently, and this is the features of Spring EL. What is Spring EL? How to use this handy feature to assist our developments? Today, we are going to learn some basics of Spring EL. Features The full name of Spring EL is Spring Expression Language, which exists in form of Java string and evaluated by Spring. It supports many syntax, from simple property access to complex safe navigation – method invocation when object is not null. And the following is the feature list from Spring EL document : ...