Search code examples
javaspringpaypal-sandboxpayout

Unable to handle paypal payout response in java


I'm trying to integrate paypal payouts feature in my spring boot app and when I get response from the API I want to check weather it was successfull or not.

The documentation says the response is going to be in json, but then in the example it has type a String.

String response = s.hasNext() ? s.next() : "";

I want simnple to read the value of batch_status from this followong response. How should I do this? how can I access values from the response? thanks

{
  "batch_header": {
    "sender_batch_header": {
      "sender_batch_id": "Payouts_2020_100007",
      "email_subject": "You have a payout!",
      "email_message": "You have received a payout! Thanks for using our service!"
     },
    "payout_batch_id": "2324242", 
    "batch_status": "PENDING"
   }
}

Well I tried to convert it to json but it did not work.

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties
public class PayOutResponse {
    private String batch_status;
    private String payout_batch_id;
}

ObjectMapper mapper = new ObjectMapper();
PayOutResponse responseObject = mapper.readValue(response, PayOutResponse.class);
String status = responseObject.getBatch_status();
error: 

Unrecognized field "batch_header" (class com.selector.model.PayOutResponse), not marked as ignorable

After updating @JsonIgnoreProperties(ignoreUnknown=true) now status is null


Solution

  • The simplest way for Jackson to access your variables is to provide getters like the following:

    @JsonIgnoreProperties(ignoreUnknown = true)
    @Getter  //option 1
    @AllArgsConstructor
    @NoArgsConstructor
    public class MyPojo {
        private String this_value;
        private String other_value;
        
    
        @Override
        public String toString() {
            return "MyPojo{" +
                    "this_value='" + this_value + '\'' +
                    ", other_value='" + other_value + '\'' +
                    '}';
        }
    }
    
    @JsonIgnoreProperties(ignoreUnknown = true)
    @AllArgsConstructor
    @NoArgsConstructor
    public class MyPojo {
        private String this_value;
        private String other_value;
    
        public String getThis_value() {  //option 2
            return this.this_value;
        }
    
        public String getOther_value() {
            return this.other_value;
        }
    
        @Override
        public String toString() {
            return "MyPojo{" +
                    "this_value='" + this_value + '\'' +
                    ", other_value='" + other_value + '\'' +
                    '}';
        }
    }
    

    Alternatively, You need to use both @JsonProperty and @JsonCreator in conjunction like so:

    @JsonIgnoreProperties(ignoreUnknown = true)
    public class MyPojo {
        private String this_value;
        private String other_value;
    
        @JsonCreator
        public MyPojo(@JsonProperty("this_value") String this_value, @JsonProperty("other_value") String other_value) {
            this.this_value = this_value;
            this.other_value = other_value;
        }
    
        @Override
        public String toString() {
            return "MyPojo{" +
                    "this_value='" + this_value + '\'' +
                    ", other_value='" + other_value + '\'' +
                    '}';
        }
    }
    

    Running the following code:

    public class Main {
        public static void main(String[] args) throws Exception {
            final var mapper = new ObjectMapper();
            final var json ="{\"this_value\":\"this\",\"other_value\":\"other\"}";
            System.out.println(json);
            System.out.println(mapper.readValue(json, MyPojo.class));
        }
    }
    

    will give an output of:

    {"this_value":"this","other_value":"other"}
    MyPojo{this_value='this', other_value='other'}
    

    I decided to actually use your sample json instead of a random sample json. Here is the code I used:

    public class MyPojo {
        @JsonProperty("batch_header")
        private myWrapper myWrapper;
    
        public MyPojo() {}
    
        public MyPojo.myWrapper getMyWrapper() {
            return this.myWrapper;
        }
    
        public void setMyWrapper(MyPojo.myWrapper myWrapper) {
            this.myWrapper = myWrapper;
        }
    
        @JsonIgnoreProperties(ignoreUnknown = true)
        @NoArgsConstructor
        class myWrapper {
            private String payout_batch_id;
            private String batch_status;
    
            public String getPayout_batch_id() {
                return this.payout_batch_id;
            }
    
            public void setPayout_batch_id(String payout_batch_id) {
                this.payout_batch_id = payout_batch_id;
            }
    
            public String getBatch_status() {
                return this.batch_status;
            }
    
            public void setBatch_status(String batch_status) {
                this.batch_status = batch_status;
            }
    
            @Override
            public String toString() {
                return "myWrapper{" +
                        "this_value='" + payout_batch_id + '\'' +
                        ", other_value='" + batch_status + '\'' +
                        '}';
            }
        }
    
        @Override
        public String toString() {
            return "MyPojo{" +
                    "myWrapper=" + myWrapper +
                    '}';
        }
    }
    
    public class Main {
        public static void main(String[] args) throws Exception {
            final var mapper = new ObjectMapper();
            final var json ="{ \"batch_header\": { \"sender_batch_header\": { \"sender_batch_id\": \"Payouts_2020_100007\", \"email_subject\": \"You have a payout!\", \"email_message\": \"You have received a payout! Thanks for using our service!\" }, \"payout_batch_id\": \"2324242\", \"batch_status\": \"PENDING\" } }";
            System.out.println(json);
            System.out.println(mapper.readValue(json, MyPojo.class));
        }
    }
    

    Output:

    { "batch_header": { "sender_batch_header": { "sender_batch_id": "Payouts_2020_100007", "email_subject": "You have a payout!", "email_message": "You have received a payout! Thanks for using our service!" }, "payout_batch_id": "2324242", "batch_status": "PENDING" } }
    MyPojo{myWrapper=myWrapper{this_value='2324242', other_value='PENDING'}}
    

    its not that the earlier code doesnt work. Its just that it wasnt applicable to your json due to nested objects.

    edit: I was checking something up, and apparently as of 1.9 Jackson provides a DeserializationFeature to unwrap the top level root elements. See the new code that is much simpler. Note the @JsonRootElement that indicates the name of the pojo that should be deserialized (without it, Jackson will look for a field named after your pojo, not batch_header)

    @JsonIgnoreProperties(ignoreUnknown = true)
    @NoArgsConstructor
    @AllArgsConstructor
    @Getter
    @JsonRootName("batch_header")
    public class MyPojo {
        private String payout_batch_id;
        private String batch_status;
    
        @Override
        public String toString() {
            return "MyPojo{" +
                    "payout_batch_id='" + payout_batch_id + '\'' +
                    ", batch_status='" + batch_status + '\'' +
                    '}';
        }
    }
    
    public class Main {
        public static void main(String[] args) throws Exception {
            final var mapper = new ObjectMapper();
            mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
            final var json ="{ \"batch_header\": { \"sender_batch_header\": { \"sender_batch_id\": \"Payouts_2020_100007\", \"email_subject\": \"You have a payout!\", \"email_message\": \"You have received a payout! Thanks for using our service!\" }, \"payout_batch_id\": \"2324242\", \"batch_status\": \"PENDING\" } }";
            System.out.println(json);
            System.out.println(mapper.readValue(json, MyPojo.class));
        }
    }