Search code examples
javaxmljaxbmarshallingxmltransient

XmlTransient annotation on getter only?


I am trying to unmarshal an XML into an object that I expect should have a certain field. However, I do not want to marshal that object into an XML that contains it. What I like would be similar to this:

@XmlRootElement(name = "User")
public class User {

    private String name;

    @XmlTransient
    public String getName() {
        return this.name
    }

    @XmlElement(name = "Name")
    public void setName(String name) {
        this.name = name
    }
}

However, this would not work due to the conflicting annotations, as I can't use any other XML annotations with @XmlTransient. I have also tried to add the @XmlTransient annotation on the field itself instead of the getter and have set this option:

XmlAccessorType(XmlAccessType.FIELD)

In addition, I kept the @XmlElement annotation on the setter, and that did absolutely nothing in terms of excluding the field from being marshalled.

I would like to keep the @XmlElement annotation, since I like being able to translate a field with a different name (here it is just a capitalization difference) into whichever field I want.

I also cannot delete the getter, as I do use it in the application.

Given that, I don't know what my options are at this point, other than writing an adapter (which I could do, but if there is another solution, I'd rather not use a custom adapter because of this one field). Any help would be greatly appreciated.


Solution

  • I think your problem lies in the idea itself: @XmlTransient tells the marshaller to completely ignore that field/property when doing its job, so I'd guess it's not what you're looking for, as you wouldn't want to (and couldn't anyway) set a custom name for the marshalled element if you wanted to omit it in the first place.

    Another point is that with JAXB, public getters/setters are paired with their respective counterparts, so the annotations applied to both are "merged" when read (hence why you can't use @XmlTransient in the getter and @XmlElement in the setter at the same time), and thus their positions are also interchangeable.

    Also, just for clarity, @XmlAccessorType only interferes in the default handling of public members. If the field or method in question is not public, it won't affect how it will be handled by default.

    Now for the solutions:

    • If you want to omit it all:

      • With private field and public getter/setter, just use @XmlTransient once in the getter or setter and nothing else.

        @XmlRootElement(name = "User")
        public class User {
        
            private String name;
        
            @XmlTransient
            public String getName() {
                return this.name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        }
        
      • If both are public, use @XmlTransient once in the field and once again in either getter or setter.

        @XmlRootElement(name = "User")
        public class User {
        
            @XmlTransient
            public String name;
        
            @XmlTransient
            public String getName() {
                return this.name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        }
        
    • If instead you want to keep it with a custom name:

      • If the field is private, use only @XmlElement once in the getter or setter.

        @XmlRootElement(name = "User")
        public class User {
        
            private String name;
        
            @XmlElement(name = "Name")
            public String getName() {
                return this.name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        }
        
      • If both field and accessors are public (and there's no @XmlAccessorType or it's set to XmlAccessType.PUBLIC_MEMBER), then you'll have to use @XmlTransient in either field or getter/setter and @XmlElement in the other (they'll be interchangeable if all the methods do is just reading/writing the value, as in this case).

        @XmlRootElement(name = "User")
        public class User {
        
            @XmlTransient
            public String name;
        
            @XmlElement(name = "Name")
            public String getName() {
                return this.name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        }