1

POJOs:

import lombok.Data;
@Data
public class CCMTRequest {
    
    private MOEH cch;
    
    private String filler1;
    
    private CCMTCCD ccd;
    
    private String uPwName;
}


@Data
public class MOEH {
    private String c;
    private int z;
    private String dType;
}


@Data
public class CCMTCCD {
    private dTime time;
    private int x;
}

@Data
public class dTime {
    private String dTime;
}

Test Class:

public class TestJacksonParser {
    
    @Test
    void load_jsonToPOJO() {
        ObjectMapper mapper = new ObjectMapper();
        ClassLoader load = this.getClass().getClassLoader();
        File file = new File(load.getResource("request.json").getFile());
        CCMTRequest req = null;
        try {
            req = mapper.readValue(file, CCMTRequest.class);
        }
        catch(Exception e) {
            System.out.println(e.getMessage());
        }
        System.out.println("\nRequest: " + req);
    }
    
}

request.json :

{
    "cch" : {
        "c" : "C",
        "z" : 4678,
        "dType" : "dtype"       
    },
    "filler1" : "random filler1",
    "ccd" : {
        "time" : {
            "dTime" : "4:35"
        },
        "x" : 34567
    },
    "uPwName" : "uPwName"
}

Error:

Unrecognized field "dType" (class com.spring.mapstruct.test.MOEH), not marked as ignorable (3 known properties: "z", "c", "dtype"]) at [Source: (File); line: 5, column: 14] (through reference chain: com.spring.mapstruct.test.CCMTRequest["cch"]->com.spring.mapstruct.test.MOEH["dType"])

Request: null

Now, when I update my test class as :

public class TestJacksonParser {
        
        @Test
        void load_jsonToPOJO() {
            ObjectMapper mapper = new ObjectMapper();
    
            //ignore Unknown JSON Fields
          mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

            ClassLoader load = this.getClass().getClassLoader();
            File file = new File(load.getResource("request.json").getFile());
            CCMTRequest req = null;
            try {
                req = mapper.readValue(file, CCMTRequest.class);
            }
            catch(Exception e) {
                System.out.println(e.getMessage());
            }
            System.out.println("\nRequest: " + req);
        }
        
    }

I get output as:

Request: CCMTRequest(cch=MOEH(c=C, z=4678, dType=null), filler1=random filler1, ccd=CCMTCCD(time=dTime(dTime=4:35), x=34567), uPwName=null)

So how jackson is working here with lombok, is there an issue with properties "dType" and "uPwName" ?

tusharRawat
  • 719
  • 10
  • 24
  • Is it important to use Lombok here? I would simply skip Lombok and make the four data classes have public fields, e.g. `public class dTime { public String dTime; }` – Erik Feb 25 '21 at 04:31
  • @Erik, yes it's important to use lombok, as request has about 200 properties and I can't make fields public – tusharRawat Feb 25 '21 at 04:33
  • Hmm, I'm not an expert on Lombok, but to me it looks like Lombok and Jackson don't agree on what the setter for `dType` should be called. From your error message, it looks like Jackson thinks your class has a property `dtype` (instead of `dType`) – Erik Feb 25 '21 at 04:49
  • 1
    `mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);` might help ... – Erik Feb 25 '21 at 04:54

1 Answers1

1

first things first, next time please provide better example rather than random name properties. it's confusing.

your problem is because lombok generate getter and setter for property like "uPwName" becomes "getUPwName()" and "setUPwName()". jackson read it as "getuPwName" and "setuPwName";

the library both using different naming convention for getters and setters.

there are 2 approach to fix this:

  1. for your quick fix:
ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
  1. for better way to fix your problem: use better name for your properties.
Ferry
  • 344
  • 3
  • 10
  • why that's not the case with other properties ? – tusharRawat Feb 25 '21 at 10:35
  • And why FAIL_ON_UNKNOWN_PROPERTIES is required ? – tusharRawat Feb 25 '21 at 10:49
  • FAIL_ON_UNKNOWN_PROPERTIES is not required. but by default jackson will throw an error if there is an unmatched properties between json and java object. you can remove it if you think you need to throw an error when there is an unmatched properties. The problem only occurs on mixed case on first 2 letters. Lombok treats every first letter as uppercase on getters and setters. and leave the second letters as it is. While on Jackson (and most getters and setters generators too), if it's mixed case on first 2 letters, they don't change it. – Ferry Feb 25 '21 at 11:12
  • Here I can configure jackson, but what if the request is getting read using annotation @RequestBody in my controller's method, in that case, spring boot will internally use jackson library, what should I do? – tusharRawat Mar 03 '21 at 15:39
  • Use the second approach by using better variable names. The convention is: it should Be clear and concise, written in English, avoid using acronym or abbreviate, and should readable and speakable. for example: use firstName instead of fName. Or use unitOfMeasurement instead of uom. but if you wanted to know how to override jackson configuration on Spring Boot, this https://www.baeldung.com/spring-boot-customize-jackson-objectmapper should help. – Ferry Mar 03 '21 at 15:51
  • If jackson reads by calling getdType(), but lombok produces getDType, then even just annotating property named "dType" with (at)JsonProperty ("dType"), deserialization/serialization is working, why? – tusharRawat Mar 08 '21 at 07:07
  • jackson will ignore your property name if you use `@JsonProperty` explicitly (or `@ConstructorProperties` if you use all arguments constructor), no matter what name it was. – Ferry Mar 08 '21 at 10:32
  • but even if you annotate property with `@JsonProperty`, Jackson must be using some setter/getter for deserialization/serialization ? – tusharRawat Mar 11 '21 at 06:46
  • when using `@JsonProprety` jackson will find which property matched with that annotation value and set it by reflection api. it doesn't based on property name anymore. even if you write any random name. – Ferry Mar 12 '21 at 05:00