Unable to handle paypal payout response in java

111 Views Asked by At

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

1

There are 1 best solutions below

7
BIBOO nation On BEST ANSWER

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));
    }
}