Jackson Wrapping Objects in List - java

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.

Related

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

JSON serialization of an object tree structure

When serializing my POJOs with relationships, I used to create different views for each of my classes. For every class, I created a view Basic, displaying only scalar properties, and Detail including in top of that all my relationships. It looks like this :
public class Stage extends BasicDomainObject {
#JsonView(Views.Stage.Basics.class)
protected String stageType = "";
#JsonView(Views.Stage.Basics.class)
protected String scheduledReleaseGraph = "";
#JsonView(Views.Stage.Details.class)
private Pipeline pipeline;
// ...
}
Then, in my REST api layer, I could serialize the correct view by specifying the right one:
mapper.writerWithView(Views.Stage.Details.class).writeValueAsString(bean);
Now, I had to add a field private Stage parentStage in my Stage class. I'm trying to have an output looking like this with my Details view :
{
"id": 2,
"type": "dev",
"scheduledReleaseGraph" "xxx",
"pipeline" : {
...
},
"parent" : {
"id": 1,
"type": "dev",
"scheduledReleaseGraph" "yyy"
}
}
The goal here is to display the parent association with only one level of depth.
What is the common pattern to achieve this ?
If you use Jackson 2.0, I would look into the JsonIdentityInfo attribute:
https://fasterxml.github.io/jackson-annotations/javadoc/2.0.0/com/fasterxml/jackson/annotation/JsonIdentityInfo.html
This annotation helps you to handle cyclic references when serializing/deserializing.

Jackson deserialization of class

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.

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