spring-mvcresttemplatecastorspring-web

SAXException on root element unmarshalling in spring web service


I have restful webservice with spring which has following mapping:

<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Object Mapping DTD Version 1.0//EN"
                         "http://Castor.exolab.org/mapping.dtd">


<mapping>
    <class name="com.example.web.Contacts">
        <field name="contacts" type="com.example.web.model.Contact"
            collection="arraylist">
            <bind-xml name="contact" />
        </field>
    </class>
    <class name="com.example.web.model.Contact" identity="id">
        <map-to xml="contact" />
        <field name="id" type="long">
            <bind-xml name="id" node="element" />
        </field>
        <field name="firstName" type="string">
            <bind-xml name="firstName" node="element" />
        </field>
        <field name="lastName" type="string">
            <bind-xml name="lastName" node="element" />
        </field>
        <field name="birthDate" type="string" handler="dateHandler">
            <bind-xml name="birthDate" node="element" />
        </field>
        <field name="version" type="integer">
            <bind-xml name="version" node="element" />
        </field>
    </class>
    <field-handler name="dateHandler"
        class="com.example.web.DateTimeFieldHandler">
        <param name="date-format" value="yyyy-MM-dd" />
    </field-handler>
</mapping>

Contacts class:

public class Contacts implements Serializable {
    private List<Contact> contacts;

    public Contacts() {
    }

    public Contacts(List<Contact> contacts) {
        this.contacts = contacts;
    }

    public List<Contact> getContacts() {
        return contacts;
    }

    public void setContacts(List<Contact> contacts) {
        this.contacts = contacts;
    }
}

Contact class:

@Entity
@Table(name = "contact")
public class Contact implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private Long id;
    private int version;
    private String firstName;
    private String lastName;
    private DateTime birthDate;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    public Long getId() {
        return id;
    }

    @Version
    @Column(name = "VERSION")
    public int getVersion() {
        return version;
    }

    @Column(name = "FIRST_NAME")
    public String getFirstName() {
        return firstName;
    }

    @Column(name = "LAST_NAME")
    public String getLastName() {
        return lastName;
    }

    @Column(name = "BIRTH_DATE")
    @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    public DateTime getBirthDate() {
        return birthDate;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public void setBirthDate(DateTime birthDate) {
        this.birthDate = birthDate;
    }

    @Override
    public String toString() {
        return "Contact - Id: " + id + ", First name: " + firstName + ", Last name: " + lastName + ", Birthday: "
                + birthDate;
    }
}

When I process GET with xml format from curl, I am receiving following output:

<?xml version="1.0" encoding="UTF-8"?>
<Contacts>
   <contacts>
      <contacts>
         <id>1</id>
         <version>0</version>
         <firstName>Chris</firstName>
         <lastName>Schaefer</lastName>
         <birthDate>
            <dayOfMonth>3</dayOfMonth>
            <dayOfWeek>7</dayOfWeek>
            <era>1</era>
            <year>1981</year>
            <dayOfYear>123</dayOfYear>
            <millisOfDay>3600000</millisOfDay>
            <monthOfYear>5</monthOfYear>
            <hourOfDay>1</hourOfDay>
            <minuteOfHour>0</minuteOfHour>
            <weekyear>1981</weekyear>
            <yearOfEra>1981</yearOfEra>
            <yearOfCentury>81</yearOfCentury>
            <centuryOfEra>19</centuryOfEra>
            <secondOfDay>3600</secondOfDay>
            <minuteOfDay>60</minuteOfDay>
            <secondOfMinute>0</secondOfMinute>
            <millisOfSecond>0</millisOfSecond>
            <weekOfWeekyear>18</weekOfWeekyear>
            <millis>357696000000</millis>
            <zone>
               <uncachedZone>
                  <fixed>false</fixed>
                  <cachable>true</cachable>
                  <id>Europe/Belgrade</id>
               </uncachedZone>
               <fixed>false</fixed>
               <id>Europe/Belgrade</id>
            </zone>
            <chronology>
               <zone>
                  <uncachedZone>
                     <fixed>false</fixed>
                     <cachable>true</cachable>
                     <id>Europe/Belgrade</id>
                  </uncachedZone>
                  <fixed>false</fixed>
                  <id>Europe/Belgrade</id>
               </zone>
            </chronology>
            <afterNow>false</afterNow>
            <beforeNow>true</beforeNow>
            <equalNow>false</equalNow>
         </birthDate>
      </contacts>
   ...

   </contacts>
</Contacts>

Maven dependencies (same for web and client app):

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.9.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.3.10.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.1-api</artifactId>
            <version>1.0.0.Final</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.190</version>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.8.1</version>
        </dependency>
        <dependency>
            <groupId> org.jadira.usertype </groupId>
            <artifactId>usertype.core</artifactId>
            <version> 3.0.0.CR3 </version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>18.0</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>4.0.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>4.0.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>4.0.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-lgpl</artifactId>
            <version>1.8.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.6.3</version>
        </dependency>

        <dependency>
            <groupId>org.codehaus.castor</groupId>
            <artifactId>castor-xml</artifactId>
            <version>1.3.3</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.castor</groupId>
            <artifactId>castor-core</artifactId>
            <version>1.3.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>2.6.3</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.3.10.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.1-api</artifactId>
            <version>1.0.0.Final</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.190</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-servlet-api</artifactId>
            <version>7.0.30</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>jstl-api</artifactId>
            <version>1.2</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>jstl-impl</artifactId>
            <version>1.2</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

Then when I am trying to get java objects (in client application) via org.springframework.web.client.RestTemplate I have following exception:

Exception in thread "main" 
org.springframework.http.converter.HttpMessageNotReadableException: 
Could not read [class com.example.web.Contacts]; nested exception is 
org.springframework.oxm.UnmarshallingFailureException: SAX reader 
exception; nested exception is org.xml.sax.SAXException: The class for 
the root element 'Contacts' could not be found. at 
org.springframework.http.converter.xml.MarshallingHttpMessageConverter.re
adFromSource(MarshallingHttpMessageConverter.java:134)
    ...

I belive that something wrong is with my mapping file. As You can see I have triple contacts tag even in mapping file I have other names.. Do anyone know what can be a reason of having this exception/wrong xml format?


Solution

  • What is happening is that when unmarshaller is looking for class he's looking for alias "Contacts" as in XML. What he has in cache is "contacts" mapping and its not the same as "Contacts". Problem was that marshaller library was not the same as unmarshaller. Probably something wrong in configuration.. What I did is change of implementation to jaxb I put @XMLRootElement and @XMLElement on model classes and completely removed configuration of messageConverters in mvc:annotation-driven (also mapping xml files). On client application I changed messageConverter to:

    <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
            <property name="messageConverters">
                <list>
                    <bean
                        class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
                        <property name="marshaller" ref="jaxb2Marshaller" />
                        <property name="unmarshaller" ref="jaxb2Marshaller" />
    
                    </bean>
                </list>
            </property>
        </bean>
        <bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
            <property name="classesToBeBound">
                <list>
                    <value>com.example.web.model.Contact</value>
                    <value>com.example.web.Contacts</value>
                </list>
            </property>
        </bean>
    

    And everything works fine.