DominoKit / domino-jackson

Jackson with Annotation processing
Apache License 2.0
53 stars 14 forks source link

Not all setters and getters forms are supported by the processor #66

Closed natros closed 11 months ago

natros commented 1 year ago

the processor can't handle

@JSONMapper
public class Foo {
  private int xY;
  public int getxY() { return xY; }
  public void setxY(int xY) { this.xY = xY; }
}

fails with the error:

FooBeanJsonDeserializerImpl.java:70: error: xY has private access in Foo
        bean.xY=value;
            ^
FooBeanJsonSerializerImpl.java:38: error: xY has private access in Foo
        return bean.xY;
                   ^

the generated code

 @Override
  protected MapLike<BeanPropertyDeserializer<Foo, ?>> initDeserializers() {
    MapLike<BeanPropertyDeserializer<Foo, ?>> map = JacksonContextProvider.get().mapLikeFactory().make();
    map.put("xY", new BeanPropertyDeserializer<Foo, Integer>() {
      @Override
      protected JsonDeserializer<?> newDeserializer() {
        return BaseNumberJsonDeserializer.IntegerJsonDeserializer.getInstance();
      }

      @Override
      public void setValue(Foo bean, Integer value, JsonDeserializationContext ctx) {
        bean.xY=value;
      }
    });
    return map;
  }

i'm using org.dominokit:domino-jackson:1.0.0 with gwt 2.10.0 and java 11

looking at the code, it seems that org.dominokit.jackson.processor.serialization.SerializerBuilder#getterInfo is looking for getXY instead of getxY

BeanInfo beanInfo = Introspector.getBeanInfo(Foo.class);
for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) {
  System.out.println(propertyDescriptor);
}
java.beans.PropertyDescriptor[name=class; values={expert=false; visualUpdate=false; hidden=false; enumerationValues=[Ljava.lang.Object;@42dafa95; required=false}; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()]
java.beans.PropertyDescriptor[name=xY; values={expert=false; visualUpdate=false; hidden=false; enumerationValues=[Ljava.lang.Object;@6500df86; required=false}; propertyType=int; readMethod=public int pt.ipb.demos.gwt.domino.jackson.client.model.Foo.getxY(); writeMethod=public void pt.ipb.demos.gwt.domino.jackson.client.model.Foo.setxY(int)]

thanks.

FrankHossfeld commented 1 year ago

I do not think that this is a bug.

The problem is related to the fact, that the getter- and setter-methods` do not follow the Java naming conventions.

Take a look here: Java Beans Spec in §7.1, §8.3.1 and §8.8.

domino-jackson is looking for getXY according to the conventions. In case there is none, it tries to access the property directly, which does not work, because it's private.

Change your getter from getxY to getXY and all will work. Of course, you have to do the same for the setter method.

natros commented 1 year ago

Hi,

Changing to getXY and setXY works but then Introspector.getBeanInfo is looking for for property XY instead of xY

@JSONMapper
public class Foo {
  private int xY;

  public int getXY() {
    return xY;
  }

  public void setXY(int xY) {
    this.xY = xY;
  }

  public static void main(String[] args) throws IntrospectionException {
    BeanInfo beanInfo = Introspector.getBeanInfo(Foo.class);
    for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
      System.out.println(pd);
    }
  }
}
java.beans.PropertyDescriptor[name=XY; 
    values={expert=false; visualUpdate=false; hidden=false; 
    enumerationValues=[Ljava.lang.Object;@56ac3a89; required=false}; propertyType=int; 
    readMethod=public int pt.ipb.demos.gwt.domino.jackson.client.model.Foo.getXY(); 
    writeMethod=public void pt.ipb.demos.gwt.domino.jackson.client.model.Foo.setXY(int)]

The getxX and setxY was generated by Intellij.

Do you think it's a bug in IntelliJ or in java itself?

Thanks.

vegegoku commented 1 year ago

Hi,

Changing to getXY and setXY works but then Introspector.getBeanInfo is looking for for property XY instead of xY

@JSONMapper
public class Foo {
  private int xY;

  public int getXY() {
    return xY;
  }

  public void setXY(int xY) {
    this.xY = xY;
  }

  public static void main(String[] args) throws IntrospectionException {
    BeanInfo beanInfo = Introspector.getBeanInfo(Foo.class);
    for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
      System.out.println(pd);
    }
  }
}
java.beans.PropertyDescriptor[name=XY; 
    values={expert=false; visualUpdate=false; hidden=false; 
    enumerationValues=[Ljava.lang.Object;@56ac3a89; required=false}; propertyType=int; 
    readMethod=public int pt.ipb.demos.gwt.domino.jackson.client.model.Foo.getXY(); 
    writeMethod=public void pt.ipb.demos.gwt.domino.jackson.client.model.Foo.setXY(int)]

The getxX and setxY was generated by Intellij.

Do you think it's a bug in IntelliJ or in java itself?

Thanks.

It is explained here

https://stackoverflow.com/questions/40822397/why-does-propertydescriptor-return-a-property-name-with-uppercase-as-first-chara

Didnt check but intellij might also have its own weird way of generating those.

natros commented 1 year ago

Hi,

Making the above changes makes domino jackson incompatible with jackson.

In the next example, domino jackson cannot read json from jackson.

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dominokit.jackson.annotation.JSONMapper;

@JSONMapper
public class Foo {
  private int xY;

  public int getXY() {
    return xY;
  }

  public void setXY(int xY) {
    this.xY = xY;
  }

  @Override
  public String toString() {
    return "xY=" + xY;
  }

  public static void main(String[] args) throws JsonProcessingException {
    Foo foo = new Foo();
    foo.setXY(555);

    // jackson
    ObjectMapper objectMapper = new ObjectMapper();
    String json = objectMapper.writeValueAsString(foo);
    System.out.println(json); // {"xy":555}

    // domino-jackson
    Foo foo1 = Foo_MapperImpl.INSTANCE.read(json); // Unknown property 'xy' in (de)serializer
    System.out.println("foo1 = " + foo1);
  }
}

or vice-versa

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dominokit.jackson.annotation.JSONMapper;

@JSONMapper
public class Foo {
  private int xY;

  public int getXY() {
    return xY;
  }

  public void setXY(int xY) {
    this.xY = xY;
  }

  @Override
  public String toString() {
    return "xY=" + xY;
  }

  public static void main(String[] args) throws JsonProcessingException {
    Foo foo = new Foo();
    foo.setXY(555);

    // domino-jackson
    String json = Foo_MapperImpl.INSTANCE.write(foo);
    System.out.println(json); // {"xY":555}

    // jackson
    ObjectMapper objectMapper = new ObjectMapper();
    Foo foo1 = objectMapper.readValue(json, Foo.class); // Unrecognized field "xY"
    System.out.println("foo1 = " + foo1);
  }
}

That means that I cannot have a FooEntity (generated) with getxY on server and a FooDto with getXY on client.

You cal also test with apache bean utils that cannot change property value BeanUtils.setProperty(foo, "xY", 123)

The §8.8 of bean spec, states that getXY -> XY and getxY -> xY

https://stackoverflow.com/a/30207335/362317