I have two java classes for parsing jason into java alone. Beyond that, the classes are not used for any thing. Below are the two classes.
import java.util.ArrayList;
public class PaymentsPaid {
public ArrayList<PaidDetailAmounts> amount;
}
and
public class PaidDetailAmounts {
public Long invoiceFeeId;
public Double amountPaid;
}
Here is where the string and the use of and object mapper.
"amount": [{"invoiceFeeId": 12085, "amountPaid": 100},{"invoiceFeeId": 12084, "amountPaid": 100},{"invoiceFeeId": 12086, "amountPaid": 500}]
and the mapper code
ObjectMapper mapper = new ObjectMapper();
try {
PaymentsPaid paymentsPaidModel = mapper.readValue(httpServletRequest.getParameter("amount"), PaymentsPaid.class);
/*
Iterator<PaidDetailAmounts> iterator = paymentsPaidModel.amount.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().invoiceFeeId);
}
*/
} catch (JsonParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
This is my exception:
org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "
invoiceFeeId" (Class PACKAGE_NAME.PaymentsPaid), not marked as ignorable
I must be doing something worng, because I built a search feature using this approach and it is currently in my application and working well. Please advise. I think it may be a mal formed json string, because it should be an array.
The Problem seams to be that jackson tryes to access the invoiceFeeId field of class PaymentsPaid, but it is a field of class PaidDetailAmounts.
I think there is some surrounding brackets missing in your Json string:
{ // <-- missing?
"amount": [{"invoiceFeeId":...}]
} // <-- missing?
But I am not an JSON expert, so I would try to write an simple test case that create a JSON string from some Java Objects and then parse this strings to back to Java Objects. So that the test can assert that both (sets of) objects are equals.
Then you can use the JSON String created by the test and compare it with your input, I would expect that the (missing) brackets are the difference between them.
Related
I have nested Object (composition) use to represent data that i want to store and using Dyanmodb enhanced client as part of AWS Java version 2 api. In the readme it explains how to flatten the objects. In the version one of the api was able to store list of objects as json documents in dyanmodb.
public class Customer{
private String name;
private List<GenericRecord> recordMetadata;
//getters and setters for all attributes
}
public class GenericRecord {
private String id;
private String details;
//getters and setters for all attributes
}
Would like it to be stored as below not flattened for backward compatibility:
{
"name": "ABC",
"recordMetadata": [
{
"id":"123",
"details":"hello"
},
{
"id":"456",
"details":"yellow"
}
]
}
https://github.com/aws/aws-sdk-java-v2/blob/master/services-custom/dynamodb-enhanced/README.md
If I understood, you want to serialize the nested object to a String, just like the #DynamoDBTypeConvertedJson annotation did with the DynamoDBMapper in v1 of the AWS SDK for Java. There is nothing that comes out of the box to do this in v2 of the AWS SDK for Java. You'll have to write your own converter by hand as shown below.
But there's really no benefit to serializing it as a String, so you may consider just storing it as a nested document. It shouldn't require any changes to the code you posted. Storing it as a document does have benefits like being able to update a single nested field. You never know when a requirement may come along that requires this, and again, I'm not aware of any downside to storing it as a document.
Note: I don't think #DynamoDbFlatten will work in your case because it doesn't make sense to flatten a list.
class GenericRecordListConverter implements AttributeConverter<List<GenericRecord>> {
private static final ObjectMapper MAPPER = new ObjectMapper();
public static GenericRecordListConverter create() {
return new GenericRecordListConverter();
}
#Override
public AttributeValue transformFrom(List<GenericRecord> input) {
try {
return AttributeValue.builder().s(MAPPER.writeValueAsString(input)).build();
}
catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
#Override
public List<GenericRecord> transformTo(AttributeValue input) {
try {
return MAPPER.readValue(input.s(), new TypeReference<List<GenericRecord>>() {});
}
catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
#Override
public EnhancedType<List<GenericRecord>> type() {
return EnhancedType.listOf(GenericRecord.class);
}
#Override
public AttributeValueType attributeValueType() {
return AttributeValueType.S;
}
}
This is resolved, don't need converter see:
.addAttribute(EnhancedType.listOf(EnhancedType.documentOf(GenericRecord.class,TableSchema.fromClass(GenericRecord.class))),
a -> a.name("recordMetadata").getter(Customer::getRecordMetadata)
.setter(Customer::setRecordMetadata) )
Official response:
https://github.com/aws/aws-sdk-java-v2/issues/2265
I am using an API from an external partner. Unfortunately, the returned response does not seem to have a fixed structure. Ideally, an API contract means it won't be violated but this keeps happening.
Anyways, so what is happening is a field in the JSON response is mostly a map but sometimes, out of the blue it is a list.
For example, suppose that following is the response I usually get:
{
"majorInfo" : {
"a" : "b"
},
"minorInfo" : {
"c" : "d"
}
}
But on rare occasion I'd get a list instead of a map or some other violation of the contract.
For example:
{
"majorInfo" : {
"a" : "b"
},
"minorInfo" : []
}
I am using jackson to map this response to a POJO. In cases, when the contract is violated, I get the error,
Exception in thread "main"
com.fasterxml.jackson.databind.JsonMappingException: Can not
deserialize instance of java.util.LinkedHashMap out of START_ARRAY
token
In this case, I lose the information in the field majorInfo as well even though that adhered to the contract. Is there any way that I can ignore a field when it does not adhere to the contract? In this case, majorInfo member of my POJO would be correctly set but minorInfo member would be null.
I know about #JsonIgnoreProperties(ignoreUnknown = true) but that would always ignore the minorInfo field. I would only like it to be ignored when the field does not adhere to the contract. Is that possible?
I also tried
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
but this did not work either.
Is any other solution possible? The external partner is not going to change their API for us that is for sure. So, any workable solution from our end?
Thank you
Edit:
One solution is I have a POJO for both variants and put the code in a try catch block. That may have worked if the JSON response had just one field that violated the contract and only in one particular way. The response I'm actually getting is huge and this is the third violation I have caught on a third field. I can't keep putting try catch blocks and by the third violation I've realized the best bet is to just ignore the fields violating it.
I found a solution using the following thread as reference:
Jackson: ignoring properties instead of throwing JsonMappingException
I wrote a custom deserializer and used it to ignore errors.
public class CustomListingDeserializer extends JsonDeserializer<Map<String, Listing>>{
public CustomListingDeserializer() {
// TODO Auto-generated constructor stub
}
#Override
public Map<String, Listing> deserialize(JsonParser arg0, DeserializationContext arg1)
throws IOException, JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = arg0.readValueAsTree();
try
{
return mapper.readValue(node.toString(), new TypeReference<Map<String,Listing>>(){});
}
catch (JsonMappingException e)
{
System.out.println("Issue in deserializing : " + e.getMessage() + "for :" + node.toString());
}
catch (Exception e)
{
throw e;
}
// TODO Auto-generated method stub
return null;
}
}
You can write your own java method for transforming an input json to a "standard" json that adheres to the contract, and then use Jackson on it.
something along the lines of:
private String transform(String input)
{
String result = input;
if (result.contains("\"minorInfo\" : []"))
{
result = result.replace("\"minorInfo\" : []", "");
}
return result;
}
I'm currently having an issue with the deserialization of certain inner-objects, in spring, I initialize all of my objects before outputting them using #ResponseBody.
As an example, this is a response:
[{id:1, location:{id:1, ... extra location data}},
{id:2, location:1}
]
Now, GSON throws an error as it is not able to understand that location:1 refers to the location object already deserialized in the previous object.
Deserialization is done in the following method:
#Override
public void handleReader(Reader reader) {
try {
String json = readerToString(reader);
T object = getGson().fromJson(json, returnType);
handleObject(object);
} catch (Exception e) {
Sentry.captureException(e);
}
}
As an example, this is called through a regular generic class, I'd use the type Event[] as the T generic in order to return an array.
How can I either fix this using Gson or make spring output the full data every time? Ideally I'd like to fix with Gson as it would allow for seriously reduced bandwidth but I'm not too fussed at this point.
My Spring returning method is as follows:
#Override
public List<T> list() {
return service.findAll();
}
with the initialization like so:
#Override
#Transactional
public List<Event> findAll() {
List<Event> list = eventRepository.findByArchivedFalse();
for (Event event : list) {
this.initialize(event);
}
return list;
}
#Override
public Event initialize(Event obj) {
Hibernate.initialize(obj.getLocation());
Hibernate.initialize(obj.getLocation().get... inner data here);
return obj;
}
I imagine this is going to require a real structure review but, if I can help it, I'd like to keep the structure roughly the same.
You're going to have to write a custom deserializer, if you're not willing to change the JSon. However, changing the JSon is exactly what I would recommend.
Option 1: Changing the JSon
I think the right thing to do is to have two separate messages, e.g.
{
"uniqueLocations":
[
{"id":1, ... extra location details} ,
],
"locationMap":
[
{"id":1,"location":1},
{"id":2,"location":1}
... etc.
]
}
This is clearer; this separates your json so that you always have the same types of data in the same places.
Option 2: Making Gson able to do more complicated deserializations
However, if you're not willing to do that, you could write a custom deserializer. The most straightforward way to do that, extending TypeAdapter, only uses specific, concrete classes, not parameterized types. However, if you want to use a parameterized type, you must use a TypeAdapterFactory.
You can read more about how to do this here: How do I implement TypeAdapterFactory in Gson?
I'm using Jackson and RESTEasy to hook into an external API. The API mainly returns simple objects which I have managed to successfully populate into POJOs.
I'm hitting a problem where I get an array of objects back e.g.
[
{
"variable1": "someValue1",
"variable2": "someValue2",
"variable3": "someValue3"
}
{
"variable1": "someValue4",
"variable2": "someValue5",
"variable3": "someValue6"
}
{
"variable1": "someValue7",
"variable2": "someValue8",
"variable3": "someValue9"
}
]
I have 2 classes: one called VariableObject which looks like this:
public class VariableObject {
private String variable1;
private String variable2;
private String variable3;
}
and VariableResponse which looks like:
public class VariableResponse {
private List<VariableObject> variableObjects;
}
My client uses JAXRS Response class to read the entity into the class i.e
return response.readEntity(VariableResponse.class);
I get a stack trace which reads:
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of VariableResponse out of START_ARRAY token
I understand you can return these as a List of POJOs i.e List quite easily, but this is not what I want to do.
The question really is two parts:
a. Can I possibly populate the VariableResponse POJO using Jackson (some how) preferably without a customer deserialiser? Maybe some annotation exists (this would be ideal)?
b. Is there some way to detect if an Array is being retuned as the root JSON node in the response and then act accordingly?
Help greatly appreciated.
Your JSON is indeed an array of objects.
You can deserialize it with:
response.readEntity(new GenericType<List<VariableObject>>() {});
And then create a new instance of VariableResponse passing resulting List as a constructor parameter like this:
public class VariableResponse {
private final List<VariableObject> variableObjects;
public VariableResponse(List<VariableObject> variableObjects) {
this.variableObject = new ArrayList<>(variableObjects);
}
}
You might forget to add comma after each {..}. After correcting your JSON string, I converted it into ArrayList<VariableObject> using TypeReference and ObjectMapper.
sample code:
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
...
TypeReference<ArrayList<VariableObject>> typeRef = new TypeReference<ArrayList<VariableObject>>() {};
ObjectMapper mapper = new ObjectMapper();
try {
ArrayList<VariableObject> data = mapper.readValue(jsonString, typeRef);
for (VariableObject var: data) {
System.out.println(var.getVariable1()+","+var.getVariable2()+","+var.getVariable3());
}
} catch (Exception e) {
System.out.println("There might be some issue with the JSON string");
}
output:
someValue1,someValue2,someValue3
someValue4,someValue5,someValue6
someValue7,someValue8,someValue9
If you prefer your own response type direct.
Try just extending ArrayList?
public VariableResponse extends ArrayList<VariableObject> {
}
Here is my Java code which is used for the de-serialization,
i am trying to convert json string into java object. In doing so i have used the following code:
package ex1jackson;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class Ex1jackson {
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
try {
String userDataJSON = "[{\"id\":\"value11\",\"name\": \"value12\",\"qty\":\"value13\"},"
+ "{\"id\": \"value21\",\"name\":\"value22\",\"qty\": \"value23\"}]";
product userFromJSON = mapper.readValue(userDataJSON, product.class);
System.out.println(userFromJSON);
} catch (JsonGenerationException e) {
System.out.println(e);
} catch (JsonMappingException e) {
System.out.println(e);
} catch (IOException e) {
System.out.println(e);
}
}
}
and my product.java class
package ex1jackson;
public class product
{
private String id;
private String name;
private String qty;
#Override
public String toString() {
return "Product [id=" + id+ ", name= " + name+",qty="+qty+"]";
}
}
i am getting the following error.
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "id" (class ex1jackson.product), not marked as ignorable (0 known properties: ]) at
[Source: java.io.StringReader#16f76a8; line: 1, column: 8] (through reference chain: ex1jackson.product["id"])
BUILD SUCCESSFUL (total time: 0 seconds)
help me to solve this,
It looks like you are trying to read an object from JSON that actually describes an array. Java objects are mapped to JSON objects with curly braces {} but your JSON actually starts with square brackets [] designating an array.
What you actually have is a List<product> To describe generic types, due to Java's type erasure, you must use a TypeReference. Your deserialization could read: myProduct = objectMapper.readValue(productJson, new TypeReference<List<product>>() {});
A couple of other notes: your classes should always be PascalCased. Your main method can just be public static void main(String[] args) throws Exception which saves you all the useless catch blocks.
You have to change the line
product userFromJSON = mapper.readValue(userDataJSON, product.class);
to
product[] userFromJSON = mapper.readValue(userDataJSON, product[].class);
since you are deserializing an array (btw: you should start your class names with upper case letters as mentioned earlier). Additionally you have to create setter methods for your fields or mark them as public in order to make this work.
Edit: You can also go with Steven Schlansker's suggestion and use
List<product> userFromJSON =
mapper.readValue(userDataJSON, new TypeReference<List<product>>() {});
instead if you want to avoid arrays.
JsonNode node = mapper.readValue("[{\"id\":\"value11\",\"name\": \"value12\",\"qty\":\"value13\"},"
System.out.println("id : "+node.findValues("id").get(0).asText());
this also done the trick.
Your product class needs a parameterless constructor. You can make it private, but Jackson needs the constructor.
As a side note: You should use Pascal casing for your class names. That is Product, and not product.