Jackson deserialization of class - java

Is it possible to deserialize the example below using Jackson?
public class A extends HashMap<String,String> {
//No other methods here for now
}
....
JSON Looks like this:
{"something":
{
"entry":
[
{"key":"one", "value":"avalue"},
{"key":"two", "value":"bvalue"}
]
}
}
...
At this time I'm getting error saying: Unrecognized Property Exception for entry.
Any help would be greatly appreciated.

First, your json is wrong, but I think I see what you're trying.
No it's not possible. HashMap<String,String> implies your object contains only top level string properties like:
{
"something": "value",
"somethingelse": "value2",
"someAdditionalThing": "value3"
}
To deserialize that you probably need to have a more strongly typed object. Jackson is falling over trying to turn:
{
"entry":
[
{"key":"one", "value":"avalue"},
{"key":"two", "value":"bvalue"}
]
}
Into a string.

Related

Jackson Wrapping Objects in List

I am trying to create a Java library to manage the Card.v1 JSON for the Alternative Runtime Google Add-ons functions. I've gotten everything to work, but am running into some problems getting Jackson to wrap classes in the same way that the Google APIs require it.
As an example, they have a Section that has a list of Widgets, which are wrapped Text Paragraphs, Images, or Button Lists. Here is an example JSON:
"sections":[ {
"widgets":[
{
"textParagraph":{
"text":"Your random cat 2:"
}
},
{
"image":{
"imageUrl":"https://cataas.com/cat"
}
},
{
"buttonList":{
"buttons":[
{
"text":"Happy",
},
{
"text":"Sad",
}
]
}
}
]
} ]
I have created a Text class, which looks like this:
#JsonTypeName("textParagraph")
#JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT, use= JsonTypeInfo.Id.NAME)
public class TextParagraph extends BaseWidget
{
String text;
}
If I use the object mapper to write this out on its own, I get the expected JSON:
"textParagraph" : {
"text" : "Testing Text"
}
But if I have a List and then print that, it loses the "textParagraph" wrapping.
{
"widgets" : [ {
"text" : "Testing Text"
}, {
"text" : "Testing Text 2"
}, {
"imageUrl" : "ggole.com/image.png",
"altText" : "Image Text"
} ]
}
Is there a special annotation I'm missing to wrap the list objects? I'd really like to do this without having to use any custom mappers or Wrapper classes.
Does anyone have experience here?
Well, I solved it.
On the base class (BaseWidget), I needed to add the #JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) annotation. With that in place, the list serializes with the wrappers.

Deserialize the JSON data encapsulated in the request body into two different model object

I am learning SpringBoot and am doing this coding challenge. So far, I have successfully set up the controller and have done the mapping.
#RestController
#RequestMapping(path="/mydomain")
public class PaymentController {
#RequestMapping(method=RequestMethod.POST, value="/ingest")
public void ingestData(#RequestBody String data) {
System.out.println("ingest Data");
System.out.println(data);
// List<Orders>
// List<Returns>
}
#RequestMapping(method=RequestMethod.GET, value="/query")
public String queryData(#RequestBody String data) {
// look at LIST of order and return..and return something
}
}
The String data is JSON and it contains two different types - Order and Return.
{
"entries": {
{
type: "ORDER",
name: "order_1",
"attributes": {
"owner": "John"
}
},
{
type: "ORDER",
name: "order_2",
"attributes": {
"owner": "Mike",
"buyer": "Bob"
}
// attributes is a key/value pair Map
},
{
type: "RETURN",
name: "return_1",
"attributes": {
"user": "kelly",
"time": "null",
"inputs": [
"USD",
"EUR"
],
"outputs": [
"CAD",
"GBP"
]
}
// attributes is a key/value pair Map
},
}
}
In ingestData(), I want to parse though the json and create 2 lists - one each for orders and returns. In the past, I have dealt with the all the items in the json mapping to the same Java class. How do I parse and map json items into 2 different java classes?
You should probably rethink your REST api setup a bit. It's better to create endpoints based on classes rather than have generic endpoints that process multiple. Although this might look like more work now it will really help you generate more maintainable code. The fact that you now run into this problem where you want an ObjectMapper to resolve to different classes from a single Json is a good indicator that you're going in the wrong direction.
There is a great REST API design best-practices here on Stack available
[JsonString] -some json parsing libraries-> [Collection<Entity>] -.stream().collect(Collectors.groupingBy())-> [Map<String(type),Collection<Entity>>]
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html

Can I have an Avro Schema with an enum that includes values as enum members?

This is the Java enum that I want to transform into an Avro Schema:
public enum ApplicationCode {
APP_A("MY-APP-A"),
APP_B("MY-APP-B");
private final String code;
ApplicationCode(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
Since enums are generally available as Types in Avro, I came up with following:
{
"type" : "enum",
"name" : "ApplicationCode",
"namespace" : "com.example",
"symbols" : [ "APP_A", "APP_B" ]
}
And referenced it in my main Avro like this:
"fields": [
{
"name": "masterApplicationCode",
"type": "ApplicationCode"
},
It works like that but unfortunately I am losing the Application Codes (e.g. "MY-APP-A") using this approach. I'm looking for something, that allows me to include both, the code and the label. Something like
{
"type" : "enum",
"name" : "ApplicationCode",
"namespace" : "com.example",
"symbols" : [ "APP_A("MY-APP-A")", "APP_B("MY-APP-B")" ]
}
Is it even possible to have this kind of complex enums or is there any workaround to achieve this?
I believe the avro schema is internally transforming it into a JSON String. So, I think the question is more about serializing enums. Reference from here - https://www.baeldung.com/jackson-serialize-enums
I think it should return the code if you use JsonFormat annotation like this -
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum ApplicationCode {
Otherwise you will need to add a Custom Serializer for enum.
I solved my problem by writing custom serializer / deserializer that map an object with complex typed fields to one that is being sent with e.g. Strings instead of enums.
Here an example of the custom serializer:
public class CustomSerializer implements Serializer<ApplicationObject> {
#Override
public byte[] serialize(String topic, ApplicationObject ApplicationObjectDto) {
com.example.avro.ApplicationObject applicationObject = com.example.avro.ApplicationObject.newBuilder()
.setApplicationCode(ApplicationObjectDto.getApplicationCode().getCode())
.build();
return SerializationUtils.serialize(applicationObject);
}
}

Converting java object having string json filed in to JSON

In our application one database table having some result stored as json as below:
---------------------------------------------------- -------------
content | other fields...
---------------------------------------------------- --------------
"{ \"key\":[\"value1\",\"value2\",\"value3\"]}" | 12 ...
I need to fetch and write into a result file , the content field of a set of records as a single json like:
(Expected)
[
{
"content": {
"key": [
"value1",
"value2",
"value3"
]
}
}
.....
]
In the orresponding java entity I put #JsonIgnore for all fields except content.
class Result{
//#JsonIgnore
//otherfields
....
#Column("content")
private String content;//the json string field
....
}
But when I read from db and write to file using:
ObjectWriter writer = new ObjectMapper().writer().withDefaultPrettyPrinter();
writer.writeValue(new File(outFile), entityList);
I got file as:
(Original)
[
{
"content" : "{ \"key\":[\"value1\",\"value2\",\"value3\"]}"
}
....
]
You may notice the issue. It take the jason field as a string and put as the value for the key "content", instead of a nested jason as expected
According to How can I include raw JSON in an object using Jackson? you can try to annotate the content with #JsonRawValue:
class Result {
#JsonRawValue
private String content;
}
This will output:
[ {
"content" : { "key":["value1","value2","value3"]}
} ]
which is semantically what you want. However, you expected the outout to be pretty formatted. This can be achieved by specifying a serializer, see also Convert JSON String to Pretty Print JSON output using Jackson :
class Result {
#JsonRawValue
#JsonSerialize(using = ToPrettyJsonSerializer.class)
private String content;
}
private static class ToPrettyJsonSerializer extends JsonSerializer<String> {
#Override
public void serialize(String string, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException {
Object json = new ObjectMapper().readValue(string, Object.class);
gen.writeObject(json);
}
}
This outputs:
[ {
"content" : {
"key" : [ "value1", "value2", "value3" ]
}
} ]
It is not exactly the format you expected, but getting close. Hope that helps.
I think, backlashes before quotation marks cause the problem and whole JSON data is treated as String instead of JSON object. You can try to remove backslashes before transforming JSON into object. One of the solutions I found is here: https://stackoverflow.com/a/19871960/1150795.
Try to use #JsonRawValue annotation.
How can I include raw JSON in an object using Jackson?

Gson - Conditional JSON Deserialization (Sanity Checks)

I have code in my android project that correctly deserializes json into POJO's using gson. I wish to add some condtional acceptance checks (sanity checks) to the incoming json. For instance, with the data structure below, I wish for the gson parser to only add objects where the start date < end date. I know I could iterate through the list after it is populated to remove invalid items, but I was wondering if there was any way to reject the items on the fly. Any help would be appreciated.
Example JSON
{
"Items" : [
{
"Name" : "Example Name",
"Start" : "2010-10-16 10:00:00",
"End" : "2011-03-20 17:00:00",
<other fields>
},
<several more items>
]
}
ItemList.java
public class ItemList {
private List<ItemHeader> Items;
public void setItemHeaders(List<ItemHeader> headers) {
Items = headers;
}
public List<ItemHeader> getItemHeaders() {
return Items;
}
}
ItemHeader.java has fields for name, start, end and all the other fields.
You'd need to write your own deserializer and have it throw an exception when your condition isn't met.
http://sites.google.com/site/gson/gson-user-guide#TOC-Writing-a-Deserializer
I don't know that you should do this, but it's possible.

Categories