Property and Alias mapping don't seem to work - java

I have a simple json like this
{
"someReports":[
{
"reportName": "PR123",
"fields": [
]
},
{
"reportName": "PR234",
"fields": []
}
]
}
I have a class that looks somewhat like this inside which getSomeReports() is defined.
class AHeckLotOfReports {
private String someString;
private List<SomethingElse> some;
..
#JsonProperty("someReports")
private List<SomeReports> someReports;
}
//POJO:
class SomeReport {
String reportName;
List<Field> fields;
...
}
//REST Controller looks like this. some injection code is cleaned up
#Api(tags = “SomeReport”)
#Controller
#ThreadSafe
#RequestMapping(“/report/v2")
public class ReportController{
#ApiOperation(value = "Create a new report.”)
#RequestMapping(value = “/report”, method = RequestMethod.POST)
#ResponseBody
public ReportResponse addReport(
#Nonnull final AuthorizationToken authorizationToken,
#Nonnull#RequestBody final AHeckLotOfReports reportRequest) {
final Report report=this.reportService.addReport(
authorizationToken,
reportRequest.getName(),
reportRequest.isEnabled(),
reportRequest.getConfiguration().or(ReportConfiguration.empty()),
reportRequest.getNewConfiguration(),
reportRequest.getDefinition());
return ReportResponse.fromReport(report);
}
I haven't been able to get this working. I do get the structure intact but reportName comes up blank, the fields array comes up empty.
I have tried JsonAlias("someReports","some_reports") and that seems to make no difference.
Anything comes out as an obvious "duh" in this ?
EDIT: My apologies, I did realize I have not provided the entire context. I AM able to deserialize the simple POJO with ObjectMapper. But the class AHeckLotOfReports is used as request object in REST endpoint for a POST and this is where the problem surfaces
Software: jackson.core 2.9.9, jackson.datatype 2.9.8 and JDK 8.0, Spring-Boot 2.2.6

Related

Spring boot RequestBody getting null

I'm new in Spring Boot, i have a little issue when i'm getting JSON in my RestController from Postman.
When i send the request from Postmat with its attributes it's always getting null in the RequestBody.
This is my Rest controller
#RestController
#RequestMapping(value="/api/enviar",produces = MediaType.APPLICATION_JSON_VALUE,
headers = {"content-type=application/json"})
#RequiredArgsConstructor
#Slf4j
public class EnviarAdicionService {
#Autowired
private final EnviarAdicionUseCase enviarAdicionUseCase;
#Autowired
private final GuardarLogUseCase guardarLogUseCase;
private final Gson gson = new Gson();
private String _statusCode;
private Date dateStart;
#PostMapping
public DatosAdicionResponse PostAdicionFondos(#RequestBody #Valid RequestAdicion requestAdicion){
//PostMapping logic
...
}
}
My RequestAdicion is like this:
#Data
#NoArgsConstructor
public class RequestAdicion implements Serializable {
private final static String regExpression = "\\{\\[sw\\.,ñÑ]\\+\\$}";
#RequestHeaderValidation
private RequestHeader Header;
#CuentaValidation(NumeroIdentificacionName = "NumeroDocumentoCuentaOrigen", TipoIdentificacionName = "TipoDocumentoCuentaOrigen",
TipoCuentaDepositosName = "TipoCuentaDepositoOrigen", NumeroCuentaDepositoName = "NumeroCuentaDepositoOrigen",
EntidadCuentaName = "EntidadCuentaOrigen", TipoCuentaName = "TipoCuentaOrigen", NumeroCuentaName = "NumeroCuentaOrigen",
CodigoFondoName = "CodigoFondoCuentaOrigen", EntidadCuentaDepositoName = "EntidadCuentaDepositoOrigen")
private CuentaOrigen CuentaOrigen;
#CuentaFondoValidation(TipoIdentificacionName = "TipoDocumentoCuentaDestino", NumeroIdentificacionFondoName = "NumeroDocumentoCuentaDestino",
EntidadName = "EntidadCuentaDestino", CodigoFondoName = "CodigoFondoCuentaDestino",
NumeroFondoInversionName = "NumeroFondoInversionCuentaDestino")
private CuentaFondo CuentaDestino;
#FormaDePagoValidation
#Pattern(regexp = regExpression,message = "Valor no permitido. FormaDePago")
private String FormaDePago;
#ValorValidation
private double ValorAdicion;
#OficinaValidation
private long Oficina;
#CanalValidation
#Pattern(regexp = regExpression,message = "Valor no permitido. Canal")
private String Canal;
}
I'm sending these attributes in Postman
Headers
Content-Type: application/json
Body
{
"Header": {
"SystemId": "AW1371",
"MessageId": "1234567890120006",
"UserName": "autWakanda",
"Destination": {
"Name": null,
"NameSpace": null,
"Operation": null
}
},
"CuentaOrigen": {
"NumeroDocumentoCuentaOrigen": 8232166,
"TipoDocumentoCuentaOrigen": "1",
"TipoCuentaDepositoOrigen": "7",
"NumeroCuentaDepositoOrigen": "40673760005",
"EntidadCuentaOrigen": "00007",
"TipoCuentaOrigen": "7",
"NumeroCuentaOrigen": "40673760005",
"CodigoFondoCuentaOrigen": "123"
},
"CuentaDestino": {
"TipoDocumentoCuentaDestino": "1",
"NumeroDocumentoCuentaDestino": 1360740,
"NumeroFondoInversionCuentaDestino": "0021008106434090",
"EntidadCuentaDestino": "00532",
"CodigoFondoCuentaDestino": "21"
},
"FormaDePago": "72",
"ValorAdicion": 133000.31,
"Oficina": 3132,
"Canal": "SVE"
}
I tried to set the first character in lowercase but it doesn't work.
This is the exception raised:
Caused by: java.lang.NullPointerException: Cannot invoke "co.com.bancolombia.model.requestheader.RequestHeader.getSystemId()" because the return value of "co.com.bancolombia.api.models.RequestAdicion.getHeader()" is null
Thank you all in advance.
EDIT: I solved the problem setting the annotation #JsonProperty to each field, and creating to each model a NoArgsConstructor initializing variables with no value and the annotation #AllArgsConstructor in the class.
I just had the same issue for a different reason, so I'm adding this quick note to maybe help someone else.
I couldn't figure why my controller parameter annotated as #RequestBody was coming thru as all blank/empty. Already checked Content-Type: application/json.
I wasted a lot of time on this before realizing my IDE had chosen a totally wrong import for that annotation class.
So just make sure you've got
import org.springframework.web.bind.annotation.*;
and not some other RequestBody class in your imports.
The problem is with your case. AFAIK, by default spring uses camelCase even if you use PascalCase (or UpperCamelCase) for your fields.
I suggest you using camelCase in stead, but if you need to stick to pascal case you should add:
#JsonProperty("Header") on your header field (and do the same for other fields.
you could also tweak the objectMapper to do this without the annotations.

Fasterxml Jackson databind MismatchedInputException

I am using com.fasterxml.jackson.databind in a spring boot application. When I send a request to my endpoint I receive the following exception:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of
org.pacakge.domain.controller.Mycontroller (although at least one Creator exists): cannot
deserialize from Object value (no delegate- or property-based Creator)\n at
[Source: (PushbackInputStream); line: 2, column: 3] Is the body of the request formatted correctly?
My controller processes a request body that has the following structure:
{
"portalId": 123,
"objectType": "TYPE",
"objectTypeId": "0-3",
"objectId": 123,
"properties": { ... }
}
The only property that I need is objectId. I've constructed a class to process this object like so:
#lombok.Value
private static class MyObject {
#JsonAlias("objectId")
private final String dealId;
}
I've designed a controller that looks like this
#Slf4j
#RestController
#RequestMapping(path = "/entrypoint")
public class MyController {
#Autowired
public MyController(){}
/**
* REST endpoint handles MyObject
*/
#PostMapping(value = "/endpoint")
public void handleRequest(
#Valid #RequestBody MyObject command
) {
log.debug(command.getDealId());
}
#lombok.Value
private static class MyObject {
#JsonAlias("objectId")
private final String dealId;
}
}
What is interesting about this problem is that my request is processed just fine if I change MyObject to the following structure:
#lombok.Value
private static class MyObject {
#JsonAlias("objectId")
private final String dealId;
private final JSONObject properties; // TODO we shouldn't need this. Fix.
}
I cannot seem to figure out what the issue is. I would love some help on this problem. Maybe there is annotation that I am missing? I am hoping someone else has experienced this issue. I haven't found any information on it by just searching the web.
I added the following line to lombok.config in the root directory of the project:
lombok.anyConstructor.addConstructorProperties=true
And after that managed to deserialize your JSON using this DTO using #JsonIgnoreProperties annotation:
#Value
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyObject {
#JsonProperty("objectId")
String dealId;
}

Can't map second level json value with Spring Webflux Webclient

I am consuming the public API for crypto currencies in Mexico: https://api.bitso.com/v3/available_books/ that returns a json like this one:
"success": true,
"payload": [
{
"book": "btc_mxn",
"minimum_price": "500.00",
"maximum_price": "16000000.00",
"minimum_amount": "0.000075",
"maximum_amount": "500.00000000",
"minimum_value": "5",
"maximum_value": "10000000.00"
},
{
"book": "eth_btc",
"minimum_price": "0.00000100",
"maximum_price": "5000.00000000",
"minimum_amount": "0.00000100",
"maximum_amount": "1000.00000000",
"minimum_value": "0.00000100",
"maximum_value": "2000.00000000"
},
and the code that consumes it with Webclient is:
#Override
public Mono<Coins> getCoins() {
return webClient.get().uri("https://api.bitso.com/v3/available_books/")
.accept(MediaType.APPLICATION_JSON)
.retrieve().bodyToMono(Coins.class);
}
The POJOs that are trying to bind it are:
#Data
public class Coins {
#JsonProperty("success")
private String success;
#JsonProperty("playload")
private List<Coin> playload;
and
#Data
public class Coin {
#JsonProperty("book")
private String book;
#JsonProperty("minimum_amount")
private String minimumAmount;
#JsonProperty("maximum_amount")
private String maximumAmount;
#JsonProperty("minimum_price")
private String minimumPrice;
#JsonProperty("maximum_price")
private String maximumPrice;
#JsonProperty("minimum_value")
private String minimumValue;
#JsonProperty("maximum_value")
private String maximumValue;
So far, it only maps like this
"success": true,
"payload": null
You need to have no-args construct and change the word playload to payload :)
I don't think this is a WebFlux issue, but rather a Jackson + Lombok issue.
What happens if you try to deserialize that payload with raw ObjectMapper?
I think Jackson requires an all args constructor annotated with #JsonCreator or ask Lombok to create a #NoArgConstructor for that class. In any case, rewriting your Coin class as a regular Java class should work.
Also, your Coins class has a typo since it's trying to get playload instead of payload.
FIXED: Typo at property name playload instead of payload

Converting a Java class with nested array of Java classes to JSON

Is there a convenient way to convert a Java class with a nested array of Java classes into JSON? For instance I want to convert an instance f the following class to JSON:
public class Students {
private final String serial_no;
private final class InnerData {
private final String[] strs;
private final String name;
private final String city;
}
private final StudentList[] students;
}
as
{
"serial_no" : null,
students : [
{
"strs" : ["athlete", "grammarian"],
"name" : "John Smith",
"city" : "Auckland"
},
{
"strs" : ["postmaster", "swimmer"],
"name" : "Jane Doe",
"city" : "Sydney"
}
]
}
What is the best way to do this in Spring? The examples I have come across seem to be simple classes so far with no nesting.
To return an java object in JSON form from an spring objects requires two simple configurations:
1) Adding 'jackson-mapper-asl' dependency to the classpath
2) Add #ResponseBody annotation to the controller's method 
1) Adding 'jackson-mapper-asl' dependency to the classpath
In a spring mvc project we need to add a 'jackson-mapper-asl' dependency to the pom.xml file, and object to json conversion is done by default.
<dependency>   <groupId>org.codehaus.jackson</groupId>   <artifactId>jackson-mapper-asl</artifactId>   <version>1.9.10</version>  </dependency>  
2) Add #ResponseBody annotation to the controller's method
Second thing we need to do is to use '#ResponseBody' annotation against the controller's method. This will make spring understand that method return value should be bound to the web response body.
#RequestMapping("studentlist")   public 
#ResponseBody   List<Student> getStudentList() {    List<Student> studentList = new ArrayList<Student>();    studentList.add(new Student(23, "Meghna", "Naidu", "meghna#gmail.com",      "8978767878"));    studentList.add(new Student(3, "Robert", "Parera", "robert#gmail.com",      "8978767878"));    studentList.add(new Student(93, "Andrew", "Strauss",      "andrew#gmail.com", "8978767878"));    studentList.add(new Student(239, "Eddy", "Knight", "knight#gmail.com",      "7978767878"));      return studentList;   
} 

Jackson Jersey JSON

I'm trying to use Jersey and Jackson (although any other way of doing JSON demarshalling works) to get this into my system in some form (be it POJO or some other representation).
Basically I only need the data section. I was trying to use GenericTypes with lists, but this is a nested list and I'm just not sure what to do. Lots of kudos for help and I really appreciate it!
{
"total": 4,
"data": [
{
"descriptor": "",
"multiInstance": false,
"active": false
},
{
"descriptor": "Apparel",
"multiInstance": true,
},
{
"descriptor": "abcd123",
"multiInstance": false,
},
{
"descriptor": "abcd",
"multiInstance": false,
}
]
}
This is the class I'm trying to use. I need a list of the class.
public class customObject {
#JsonProperty(value = "descriptor")
private String descriptor;
#JsonProperty(value = "multiInstance")
private Boolean multiInstance;
//getters and setters
}
Edit:
I'm using it in here.
CustomObjectResponse WDCOResponse =
resource
.type(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header("Authorization", getToken()).get(WDCOResponse.class);
But it's still not working.
Edit2:
Figured this out! Thanks to everyone. :)
I had to add annotation to tell it to ignore if something wasn't found, some of the JSON I'm getting back was not fully-formed in that not all fields were absolutely neccesary.
If you the object you provided is what you are passing to your controller, then you will need one more wrapper object to contain the list like this:
public class CustomRequest {
#JSonProperty(value = "total");
private Integer total;
#JsonProperty(value = "data")
private List<CustomObject> data;
// getters/setters
}
public class CustomObject {
#JsonProperty(value = "descriptor")
private String descriptor;
#JsonProperty(value = "multiInstance")
private Boolean multiInstance;
// getters/setters
}
Then your controller will just have annotations that show that the RequestBody is the CustomRequest class:
#Controller
public class JSONController {
#RequestMapping(value="sendData")
public #ResponseBody CustomResponse sendData(
#RequestBody CustomRequest request)
{
return null;
}
}
If you are still getting errors, please provide detailed error or problem. Thanks!
You'd use POJO like:
public class Response {
int count;
List<customObject> data;
}
and access the data from there:
for (customObject ob : response.data) {
// process ig
}

Categories