6

To marshal a long primitive type using JAXB, I have used @XmlJavaTypeAdapter annotation, which will adapt non-String type to a String. Even though it throw error for long type. Why is it so? How can I do marshalling on my long id attribute?

User.java

class User {
    @XmlID
    @XmlJavaTypeAdapter(WSLongAdapter.class)
    private long id;
    // Other variables
    // Getter & Setter method
}    

WSLongAdapter.java

    public class WSLongAdapter extends XmlAdapter<String, Long>{
        @Override
        public String marshal(Long id) throws Exception {
            if(id==null) return "" ;
            return id.toString();
        }
        @Override
        public Long  unmarshal(String id) throws Exception {
        return  Long.parseLong(id);
        }
     }

MarshallTest.java

public static void main(String[] args) {
    try{
        JAXBContext jaxbContext= JAXBContext.newInstance(User.class);
        Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
        OutputStreamWriter writer = new OutputStreamWriter(System.out);
        // Manually open the root element
        writer.write("<user>");
        // Marshal the objects out individually
        marshaller.marshal(new User(), writer);
        // Manually close the root element
        writer.write("</user>");
        writer.close();
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

Error:

  com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 4 counts of IllegalAnnotationExceptions
Adapter com.v4common.shared.util.other.WSLongAdapter is not applicable to the field type long. 
    this problem is related to the following location:
        at @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(type=class javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter$DEFAULT, value=class com.v4common.shared.util.other.WSLongAdapter)
        at private long com.v4common.shared.beans.usermanagement.User.id
        at com.v4common.shared.beans.usermanagement.User
Property "id" has an XmlID annotation but its type is not String.
    this problem is related to the following location:
        at private long com.v4common.shared.beans.usermanagement.User.id
        at com.v4common.shared.beans.usermanagement.User
There are two properties named "id" 
Ypsilon
  • 122
  • 1
  • 9
bNd
  • 7,512
  • 7
  • 39
  • 72

4 Answers4

8

Specify the primitive class in the annotation:

@XmlJavaTypeAdapter(type=long.class, value=WSLongAdapter.class)
Glenn Lane
  • 3,892
  • 17
  • 31
  • This solution worked for me to map a boolean to an empty object with the aim of rendering true as and false as nothing. – Ed Randall Jun 15 '14 at 09:57
5

The following might work:

class User {
    @XmlID
    @XmlJavaTypeAdapter(WSLongAdapter.class)
    @XmlElement(type=Long.class)
    private long id;
    // Other variables
    // Getter & Setter method
}    
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • Thanks, But still same error. :( .is it not possible to marshal a long type? – bNd Dec 27 '12 at 10:31
  • 1
    @Bhumika - It is possible marshal a `long` type with a JAXB (JSR-222) implementation. The problem you are seeing is that the reference impl does not like the `@XmlID` annotation on a non-String type (this is ok with EclipseLink JAXB (MOXy)). You only need to use `@XmlID` in combination with `@XmlIDREF` (see:http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html) is this what you are trying to do? – bdoughan Dec 27 '12 at 11:20
  • Ok, Actually I want to achieve one to many relationship. i.e. User has a organization, and I am facing issue like "A cycle is detected in the object graph. This will cause infinitely deep XML". so I found a solution like "through the help of `@XmlIDREF` & `@XMLID`, JAXB will use `object id` instead of `object` while generating xml graph". But I am stuck on long primitive as post. So I am trying to use adapter but still same issue. – bNd Dec 27 '12 at 11:50
  • 1
    @Bhumika - Can you change the field to be `Long` instead of `long`? You can still keep the get/set methods as `long`. – bdoughan Dec 27 '12 at 12:10
  • I know adapter works only with objects, so I have tried already this but It is not possible to change because It is entity class of hibernate. And In My project, approximate 50 or more classes+project code. This become big change for me. and it is not possible. Once I achieve for one class so I can apply for all classes. is there any other way? or It is bottleneck for me. – bNd Dec 27 '12 at 12:21
  • When I changed Long, it throws error: org.hibernate.PropertyAccessException: Exception occurred inside getter of com.v4common.shared.beans.usermanagement.User.id – bNd Dec 27 '12 at 12:25
  • 2
    @Bhumika - You could use EclipseLink MOXy as your JAXB (JSR-222) provider then you could add the `@XmlID` annotation on a `long` field (Note: I'm the MOXy lead): http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html – bdoughan Dec 29 '12 at 11:19
  • WIth MOXy _2.6.x_, you may also want to add `@XmlIDExtension` as described in http://stackoverflow.com/questions/29564627/does-moxy-support-non-string-xmlid-in-version-2-6-0 (if the adapter is not to be used) – Stepan Vavra Apr 10 '15 at 20:10
1

Create new getter annotated with @XmlID that returns String

@XmlAccessorType(XmlAccessType.FIELD)
class User {

    private long id;

    @XmlID
    public String getReferenceId() {
        return Long.toString(id);
    }

}

Only disadvantage of this solution is an additional tag is present in serialized XML (in this case <referenceId>). I tried to remove it by annotating that getter with @XmlTransient but then I got error informing that ID does not exists in referenced class.

alek
  • 11
  • 1
0

Just replace

 @XmlElement(type=Long.class)
private long id;

with

    @XmlSchemaType(name = "long")
protected Long id;
Bassel Kh
  • 1,941
  • 1
  • 19
  • 30