jaxb
  1. jaxb
  2. JAXB-251

Getter and Setter can't be in different class in the hierarchy

    Details

    • Issuezilla Id:
      251

      Description

      The problem Im having is that JAXB doesnt recognize a setter for a property if
      the setter parameter is a not exactly of the same as the parameter of the getter
      but rather a "parent-class".

      for Example, when i have the following classes:

      class A{
      }

      class B extends A{
      A parent;
      void setParent(A parent)

      {...}
      abstract A getParent();

      }

      class C extends B{
      @XMLIDREF
      B getParent(){...}

      }

      JAXB doesnt recognize the setter inside A as the right setter for "parent".

      After going over the JAXB source code i found that the cause is that
      ClassInfoImpl.collectGetterSetters() method has the following code:

      =========================
      for (M setter : propSetters) {
      T setterType = nav().getMethodParameters(setter)[0];
      if (setterType.equals(getterType)) {
      setters.put(propName, setter);
      break;
      }
      }
      ========================
      Since setterType.equals(getterType)==false, JAXB wont find setParent() as a setter.

        Activity

        Hide
        kohsuke added a comment -

        I think this issue has been fixed some time ago.

        Show
        kohsuke added a comment - I think this issue has been fixed some time ago.
        Hide
        mosh added a comment -

        I dont think so. At leat not with the version from 18/10/2006.
        Look at the the following code at ClassInfoImp.java:

        // Match getter with setters by comparing getter return type to setter param
        for (Map.Entry<String,M> entry : getters.entrySet()) {
        String propName = entry.getKey();
        M getter = entry.getValue();
        List<M> propSetters = allSetters.remove(propName);
        if (null == propSetters)

        { //no matching setter continue; }

        allSetters is only from this class and not from its super-classes (unless the
        super-class is @XmlTransient).

        I've created a function that also seeks for setters in the super-class.
        I can submit it as a patch. What is the procedure?

        Thanks!
        Mosh

        ////////////////////////////////////
        // This is the method BTW:

        List<M> findSetterInSuperClass(C c, String propName){
        List<M> propSetters = null;

        Collection<? extends M> methods = nav().getDeclaredMethods(c);
        for( M method : methods ) {
        if(nav().isBridgeMethod(method))
        continue; // ignore

        String name = nav().getMethodName(method);
        int arity = nav().getMethodParameters(method).length;

        // TODO - is needed????
        if(nav().isStaticMethod(method))

        { ensureNoAnnotation(method); continue; }

        // is this a set method?
        String propNameFromSetter = getPropertyNameFromSetMethod(name);
        if(propNameFromSetter!=null && arity==1) {
        if(propNameFromSetter.equals(propName)){
        if(propSetters == null)

        { propSetters = new ArrayList<M>(); }

        propSetters.add(method);
        }
        }
        }

        if(propSetters != null)

        { return propSetters; }

        else

        { // If we havent found the getter - going up to its super-class C sc = nav().getSuperClass(c); if((sc==null) || (sc==Object.class)) return null; else return findSetterInSuperClass(sc,propName); }

        }

        Show
        mosh added a comment - I dont think so. At leat not with the version from 18/10/2006. Look at the the following code at ClassInfoImp.java: // Match getter with setters by comparing getter return type to setter param for (Map.Entry<String,M> entry : getters.entrySet()) { String propName = entry.getKey(); M getter = entry.getValue(); List<M> propSetters = allSetters.remove(propName); if (null == propSetters) { //no matching setter continue; } allSetters is only from this class and not from its super-classes (unless the super-class is @XmlTransient). I've created a function that also seeks for setters in the super-class. I can submit it as a patch. What is the procedure? Thanks! Mosh //////////////////////////////////// // This is the method BTW: List<M> findSetterInSuperClass(C c, String propName){ List<M> propSetters = null; Collection<? extends M> methods = nav().getDeclaredMethods(c); for( M method : methods ) { if(nav().isBridgeMethod(method)) continue; // ignore String name = nav().getMethodName(method); int arity = nav().getMethodParameters(method).length; // TODO - is needed???? if(nav().isStaticMethod(method)) { ensureNoAnnotation(method); continue; } // is this a set method? String propNameFromSetter = getPropertyNameFromSetMethod(name); if(propNameFromSetter!=null && arity==1) { if(propNameFromSetter.equals(propName)){ if(propSetters == null) { propSetters = new ArrayList<M>(); } propSetters.add(method); } } } if(propSetters != null) { return propSetters; } else { // If we havent found the getter - going up to its super-class C sc = nav().getSuperClass(c); if((sc==null) || (sc==Object.class)) return null; else return findSetterInSuperClass(sc,propName); } }
        Hide
        kohsuke added a comment -

        Yes, if you can provide us a patch, that would be great!

        See https://glassfish.dev.java.net/public/GovernancePolicy.html#SCA_Policy and
        please send us the SCA so that we can accept a patch. Otherwise, just go create
        a patch and post the diff to the issue please.

        You'll only need to make sure that the patch will work for you. We'll run it
        through the tests on our side as well.

        Show
        kohsuke added a comment - Yes, if you can provide us a patch, that would be great! See https://glassfish.dev.java.net/public/GovernancePolicy.html#SCA_Policy and please send us the SCA so that we can accept a patch. Otherwise, just go create a patch and post the diff to the issue please. You'll only need to make sure that the patch will work for you. We'll run it through the tests on our side as well.
        Hide
        rebeccas added a comment -

        Implementation complete.

        Show
        rebeccas added a comment - Implementation complete.
        Hide
        desruisseaux added a comment -

        Is this improvement really done? I tried the following code snippet. It work well if the argument type of the setValue method is Double, but doesn't work anymore if I relax it to Number:

        import java.io.*;
        import javax.xml.bind.JAXBContext;
        import javax.xml.bind.annotation.*;
        
        @XmlRootElement
        public class SetterWithParentClass {
            private double value;
        
            @XmlElement
            public Double getValue() {
                return value;
            }
        
            // Argument type should be Double - testing with a relaxed type.
            public void setValue(Number n) {
                value = n.doubleValue();
            }
        
            public static void main(String[] args) throws Exception {
                JAXBContext c = JAXBContext.newInstance(SetterWithParentClass.class);
                SetterWithParentClass test = new SetterWithParentClass();
                test.value = 3;
                StringWriter out = new StringWriter();
                c.createMarshaller().marshal(test, out);
                String xml = out.toString();
                System.out.println(xml);
        
                // Unmarshall
                StringReader in = new StringReader(xml);
                test = (SetterWithParentClass) c.createUnmarshaller().unmarshal(in);
                System.out.println("value = " + test.value);
            }
        }
        

        Output is (adding indentation for clarity):

        <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
        <setterWithParentClass>
          <value>3.0</value>
        </setterWithParentClass>
        
        value = 0.0
        

        I would have hopped a value of 3.0, as we get when the setter method is setValue(Double).

        Show
        desruisseaux added a comment - Is this improvement really done? I tried the following code snippet. It work well if the argument type of the setValue method is Double , but doesn't work anymore if I relax it to Number : import java.io.*; import javax.xml.bind.JAXBContext; import javax.xml.bind.annotation.*; @XmlRootElement public class SetterWithParentClass { private double value; @XmlElement public Double getValue() { return value; } // Argument type should be Double - testing with a relaxed type. public void setValue( Number n) { value = n.doubleValue(); } public static void main( String [] args) throws Exception { JAXBContext c = JAXBContext.newInstance(SetterWithParentClass.class); SetterWithParentClass test = new SetterWithParentClass(); test.value = 3; StringWriter out = new StringWriter(); c.createMarshaller().marshal(test, out); String xml = out.toString(); System .out.println(xml); // Unmarshall StringReader in = new StringReader(xml); test = (SetterWithParentClass) c.createUnmarshaller().unmarshal(in); System .out.println( "value = " + test.value); } } Output is (adding indentation for clarity): <?xml version= "1.0" encoding= "UTF-8" standalone= "yes" ?> <setterWithParentClass> <value> 3.0 </value> </setterWithParentClass> value = 0.0 I would have hopped a value of 3.0, as we get when the setter method is setValue(Double) .
        Hide
        Iaroslav Savytskyi added a comment -

        I think this is not a valid issue.

        If JAXB is working with JavaBeans-style classes its expected that properties should have getters and setters of the same type. Which is not true in this case.

        Show
        Iaroslav Savytskyi added a comment - I think this is not a valid issue. If JAXB is working with JavaBeans-style classes its expected that properties should have getters and setters of the same type. Which is not true in this case.
        Hide
        desruisseaux added a comment -

        This issue is not incompatible with JavaBeans-style classes. It is a relaxed interpretation - not everyone want the JavaBeans constraints. In my case, I have about 100 classes derived from the ISO 19115 international standard, and I would like to design the API for ease of use rather than JAXB constraints. In its current form, the API forces the users to perform themselves a lot of wrapping toward some specialized interfaces (InternationalString - which is specific to the project) while the setters could accept a more generic interface (CharSequence - which would allow the users to specify a plain String) and do the wrapping themselves.

        If the JavaBeans restriction is still considered necessary, some annotation allowing us to specify that a method is the setter of a property would be appreciated. Maybe we could reuse the current @Element annotation, which would be allowed to be applied to setter methods.

        An alternative is to declare setter methods twice: one JavaBeans-compliant method and one convenience method. But this approach bloat the API and binary file for few purpose. I currently don't do that since I think it would bloat too much a collection of classes of the size of ISO 19115.

        Show
        desruisseaux added a comment - This issue is not incompatible with JavaBeans-style classes. It is a relaxed interpretation - not everyone want the JavaBeans constraints. In my case, I have about 100 classes derived from the ISO 19115 international standard, and I would like to design the API for ease of use rather than JAXB constraints. In its current form, the API forces the users to perform themselves a lot of wrapping toward some specialized interfaces ( InternationalString - which is specific to the project) while the setters could accept a more generic interface ( CharSequence - which would allow the users to specify a plain String ) and do the wrapping themselves. If the JavaBeans restriction is still considered necessary, some annotation allowing us to specify that a method is the setter of a property would be appreciated. Maybe we could reuse the current @Element annotation, which would be allowed to be applied to setter methods. An alternative is to declare setter methods twice: one JavaBeans-compliant method and one convenience method. But this approach bloat the API and binary file for few purpose. I currently don't do that since I think it would bloat too much a collection of classes of the size of ISO 19115.

          People

          • Assignee:
            Unassigned
            Reporter:
            mosh
          • Votes:
            2 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated: