I am getting the following object from a different service and I need to parse it into a POJO I have in my service. The incoming object looks like:
AddressMessage.java
public class AddressMessage {
#NotEmpty
#JsonProperty("id")
private String id;
#NotEmpty
#JsonProperty("address")
private String address;
public String getId() {
return this.id;
}
public String getAddress() {
return this.address;
}
#SuppressWarnings("unchecked")
#JsonProperty("data")
public void unpackNested(Map<String, Object> rawMessage) {
Map<String, String> data = (Map<String, String>) rawMessage.get("data");
this.id = (String) data.get("id");
this.address = (String) data.get("address");
}
}
and the incoming data looks like:
{“data”: { “id” : “sampleId”, “address”: “sampleAddress” }}
I've tried parsing the incoming json string to a plain Object but that didn't work:
private void publishMessage(String rawMessage, AMQP.BasicProperties props) throws IOException {
Object json = objectMapper.readValue(rawMessage, Object.class);
log.debug(objectMapper.writeValueAsString(json));
I also tried taking in the raw message and mapping it directly to the class using the object mapper like this:
private void publishMessage(String rawMessage, AMQP.BasicProperties props) throws IOException {
AddressMessage addressMessage = objectMapper.readValue(rawMessage,
AddressMessage.class);
}
Exception for above code:
Unexpected character ('“' (code 8220 / 0x201c)): was expecting double-quote to start field name
I want to pull the "id" and "address" properties out of the object but I keep running into exception due to parsing errors. Some guidance will be greatly appreciated, thanks!
Addition: I am not currently using the "unpackNested" method but thought i'd throw it in there in case it sparks any idea
You can use the #JsonCreator annotation on a constructor like this:
#JsonCreator
public AddressMessage(#JsonProperty("data") Map<String, Object> rawJson) {
this.id = rawJson.get("id").toString();
this.address = rawJson.get("address").toString();
}
This tells Jackson to use that constructor when deserializing.
Related
I have a problem during the deserialization of a response. Let's suppose I have this response from third party using webclient .
Response :
{
"name":"FirstName",
"type":"Steel",
"Fee":{
"id":"1234",
"name":"FeeFirstName"
},
"address":"2nd Street"
}
This is how my pojo classes looks like
public class Fee{} //generic OR empty class
public class Foo{
private String name;
private String type;
private Fee fee;
private String address;
}
My webclient get response code :
#Autowired
private WebClient fooWebClient;
public Foo getFoo()
{
try{
return fooWebClient.get()
.uri(uriBuilder -> uriBuilder.path("/foo/fee").build("123"))
.header(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Foo.class)
.block();
}catch(Exception e){throw new ApiClientException(e.getMessage());}
}
The above webclient getFoo() code is not giving me the full response, the Fee is coming blank stating "Class has no fields". Rest of the values are coming properly in response.
Fee needs to be empty as any other object can also come.
Please let me know how to deserialize the whole response.
You don't need the Fee class, you can get rid of it entirely and use a Map instead:
public class Foo {
private String name;
private String type;
private Map<String, Object> fee;
private String address;
}
We cannot dynamically create POJO and hence we are left with two options.
Add necessary fields to the 'Fee' class (If you know Fee structure upfront)
If you are not sure about the 'Fee' structure go for Map.
Because spring integrates Jackson you can create a custom Jackson JSON Deserializer for the Fee class that gives you more control:
#JsonDeserialize(using = FeeDeserializer.class)
public class Fee {
private String id;
private String name;
public Fee(String id, String name) {
this.id = id;
this.name = name;
}
}
import com.fasterxml.jackson.*;
public class FeeDeserializer extends JsonDeserializer<Fee> {
#Override
public Fee deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
ObjectCodec codec = jsonParser.getCodec();
JsonNode tree = codec.readTree(jsonParser);
JsonNode id = tree.get("id");
JsonNode name = tree.get("name");
return (id != null && name != null) ? new Fee(id.asText(), name.asText()) : null;
}
}
For more details see
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.json.jackson.custom-serializers-and-deserializers
https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-codecs-jackson
I am using Quarkus to receive messages from Kafka.
When I use the method with just one class, deserialization happens normally.
When my class is subclassed, I can't continue with the deserialization and an error occurs.
My Input in console kafka:
{"id":"73707ad2-0732-4592-b7e2-79b07c745e45","currentstep":"Debit-approval","payload": "{\"idCard\": 2,\"balance\": 456,\"pin\":222}","sagastatus": "STARTED","stepstatus": "{\"credit-approval\":\"STARTED\"}","type":"order-placement","version": 1}
My Method.
#Incoming("process-transaction-in")
public void process(TransactionModel transaction) throws InterruptedException { }
my deserialize class
import io.quarkus.kafka.client.serialization.ObjectMapperDeserializer;
public class TransactionDeserializer extends ObjectMapperDeserializer<TransactionModel> {
public TransactionDeserializer() {
super(TransactionModel.class);
}
My class Model
public class TransactionModel {
public TransactionModel(String id,
String currentStep,
PayloadModel payload,
String sagaStatus,
String stepStatus,
String type,
String version) {
this.id = id;
this.currentStep = currentStep;
this.payload = payload;
this.sagaStatus = sagaStatus;
this.stepStatus = stepStatus;
this.type = type;
this.version = version;
}
public String id;
public String currentStep;
public PayloadModel payload;
public String sagaStatus;
public String stepStatus;
public String type;
public String version;
public TransactionModel() {
payload = new PayloadModel();
}
}
}
The Class PayloadModel
public class PayloadModel {
public PayloadModel(String idCard,
String current,
String pin)
{
this.idCard = idCard;
this.current = current;
this.pin = pin;
}
public String idCard;
public String current;
public String pin;
public PayloadModel() {}
}
Error:
SRMSG18249: Unable to recover from the deserialization failure (topic: process-transaction), configure a DeserializationFailureHandler to recover from errors.: java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of payment.model.PayloadModel (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"idCard": 2,"balance": 456,"pin":222}')
I followed the following tutorial: https://quarkus.io/guides/kafka#kafka-serialization
Has anyone experienced this problem?
As you can see in the error log Jackson finds at least one constructor for PayloadModel class but it's not the one it's expecting since the "payload" parameter in your Kafka payload is a string and not a JSON Object. Try to change the way you serialize your data so that payload is serialized as an object.
Sorry for posting this as a response I don't have enough reputation to comment.
Say I have a String or JSON representation of a class as:
{
"id":123,
"address":{
"street":"Baker",
"city":"London"
}
}
& I want to map this JSON to a POJO which I have as following:
public class OrderDTO {
private Integer id;
private Address orderAddress;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Address getOrderAddress() {
return orderAddress;
}
public void setOrderAddress(Address orderAddress) {
this.orderAddress = orderAddress;
}
}
I am using ModelMapper for doing this and using PropertyMap<JsonElement,OrderDTO> to write the mapping for the property which is different in the POJO from the JSON. The implementation is as:
PropertyMap<JsonElement,OrderDTO>orderMap = new PropertyMap<JsonElement, OrderDTO>() {
#Override
protected void configure() {
map().setOrderAddress(this.<Address>source("address"));
}
};
However, while executing I get the following Exception:
Exception in thread "main" org.modelmapper.ConfigurationException: ModelMapper configuration errors:
1) The source path address is invalid: com.google.gson.JsonObject.address cannot be resolved.
1 error
at org.modelmapper.internal.Errors.throwConfigurationExceptionIfErrorsExist(Errors.java:241)
at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:244)
at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:96)
at org.modelmapper.internal.TypeMapImpl.addMappings(TypeMapImpl.java:92)
I am not finding any resource which I can refer to for mapping from String/JSON type to POJO. What could be the issue here?
Thanks.
Ok. I've used response = restTemplate.exchange(url, HttpMethod.POST,request, String.class) to send some request to resource server. Under that, I have used System.out.println("Response------" + response.getBody()); . With that executed, I'm receiving a string that is at the same time some kind of JSON object. Can you explain to me how can I convert it into some kind of entity so I can store it into my database? Thanks in advance!
That string that I'm receiving is like this: {"access_token":"example...","expires_in":28800,"refresh_token":"example...","scope":"example...","token_type":"Bearer","user_id":"example..."}
You can use ObjectMapper from Jackson(com.fasterxml.jackson.databind.ObjectMapper)
ObjectMapper objectMapper = new ObjectMapper();
YourClass obj = objectMapper.readValue(yourJsonString, YourClass.class);
Try using Gson:
Gson gson = new Gson();
QuestionAnswerDTO questionAnswerDTOs = gson.fromJson(json, new TypeToken<QuestionAnswerDTO>() {
}.getType());
Here QuestionAnserDTO contains the same field as that of in JSON String.
You need to create the desired entity with jaxb, like:
#XmlRootElement
public final class Entity {
private Integer id;
private String name;
public final Integer getId() {
return this.id;
}
public final void setId(Integer id) {
this.id = id;
}
public final String getName() {
return this.name;
}
public final void setName( String name) {
this.name = name;
}
}
And then you can do simple like:
response = restTemplate.exchange(url, HttpMethod.POST,request, Entity.class);
I am having a really hard time understanding how to place a mapped valued into an array and how to iterate that array.
test.json
[
{
"Name": "Bob",
"Nationality": "",
"City": "Chicago",
"Phone" : "451232424234"
},
......continues with more objects
]
testTemplate.java
//imports
#JSONInclude(Json.Include.Non_NULL)
#JsonPropertyOrder({"Name,"Nationality","City","Phone"})
Public class testTemplate {
#JsonProperty("Name")
private String userName;
#JsonProperty("Nationality")
private String nation;
#JsonProperty("City")
private String city;
#JsonProperty("Phone")
private String phone;
#JsonProperty("Name")
public String getName (String userName) {
this.userName = userName;
}
#JsonProperty("Nationality")
public String nation (String nation) {
this.nation = nation;
}
#JsonProperty("City")
public String city (String city) {
this.city = city;
}
#JsonProperty("Phone")
public String phone (String phone) {
this.phone = phone;
}
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
testParse.java
Public Class testParse {
List<testParse> test;
ObjectMapper mapper;
protected void setUp() throws IOException {
mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT();
test = mapper.readValue(this.getClass().getResourcesAsStream("test.json"),
mapper.getTypeFactory().constructCollectionType(List.class, testParse.class));
I need to help first clarifying exactly what the code is doing, and how to put JSON properties (Name, Nationality,City,Phone) into Java.
My understanding is the testTemplate file create the strings in which the properties will be held, then the testParse file has the mapper feature (from Jackson) that reads through the json and puts all into "test" (as an array?)
My goal is in testParse, where if everything thing is in "test", then I read through that, and start to pull it out and place it into a folderList.
public static Map<String, String> userName throws IOException {
Map<String, String> folderList = new TreeMap<>();
//Don't know how, but iterate through "test"
LineIterator it = new LineIterator(//the method used to read the json file);
try {
while(it.hasNext()) {
String line = it.nextLine();
folderList.put(userName,nation) //can I also put city and phone at once as well or should I create another put: folderList.put(userName, city)
return folderList;
How does one do this? Is there a better way to put the properties of json into the folderList after using the jackson mapper?
Actually, testTemplate don't generate anything, what Jackson have done here is just get data from "test.json" as String, then using Reflection read the format of testTemplate.java. Based on template it've just have + setting you add to ObjectMapper object, Jackson will convert test.json to array of object Java.
P/S: you don't need to add annotation in both attributes and get function of POJO class, just do it in get function or attributes only, it's enough for Jackson.