generic class dynamic initialization(with spring RestTemplate) - java

I am currently common api client util using spring rest template.
Our api result like following..
single result :
{
"data" : {
"id" : "...",
"name" : "..."
...
}
}
multiple result :
{
"data" : {
"cnt" : 30,
"list" : [
{
"id" : "...",
"name" : "..."
...
},
{
"id" : "...",
"name" : "..."
...
},
{
"id" : "...",
"name" : "..."
...
}
...
]
}
}
And I made two common response class like following
public class Response<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
and
public class ListResponse<T> {
private long cnt;
private List<T> list;
public long getCnt() {
return cnt;
}
public void setCnt(long cnt) {
this.cnt = cnt;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}
and example of using RestTemplate is
public <T> T apiCaller(String api){
T result= restTemplate.exchange(api,
HttpMethod.GET,
new HttpEntity<>(headers),
new ParameterizedTypeReference<Response<T>>(){}).getBody().getData();
return result;
}
and then I used,
when the result is single,
UserResponse user = apiRequest.<UserResponse>apiCaller("/users/1");
when the result is multiple,
ListResponse<UserResposne> users = apiRequest.<ListResponse<UserResponse>>apiCaller("/users");
But it also doesn't work.
it occur java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.joont.Response.ListResponse.
So I searched so much in google, but I didn't search how...
How can I solve this problem?
and I did it wrong? is it impossible?

Related

How have Jackson use toString method if object have no public accessors?

I am looking for a solution that have Jackson use toString method whenever it can not serialize an object type.
Let me explain more detail.
I have a class:
#AllArgsConstructor
public class TestJackson {
public String RequestId;
public AntiSerialize foo;
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public LocalDateTime dateRequest;
public Map<String, Object> headers;
private static class AntiSerialize {
#Override
public String toString() {
return "AntiSerialize " + ZonedDateTime.now().toEpochSecond();
}
}
public static TestJackson createSample() {
return new TestJackson(
"123",
new TestJackson.AntiSerialize(),
LocalDateTime.now(),
Map.of("content", 999,
"b3", new TestJackson.AntiSerialize(),
"b4", Arrays.asList(
new TestJackson.AntiSerialize(),
new TestJackson.AntiSerialize()
)
)
);
}
}
This is to test serialize object
var OM = new ObjectMapper()
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
try {
//var f = TestJackson.createSample().foo;
//out.println(((Object)f).toString());
var json = OM.writerWithDefaultPrettyPrinter()
.writeValueAsString(TestJackson.createSample());
out.println(json);
} catch (Exception e) {
e.printStackTrace(out);
}
It prints
{
"RequestId" : "123",
"foo" : { },
"dateRequest" : "2022-08-04 09:04:14",
"headers" : {
"b3" : { },
"b4" : [ { }, { } ],
"content" : 999
}
}
But I expect:
{
"RequestId" : "123",
"foo" : "AntiSerialize 1659578741",
"dateRequest" : "2022-08-04 09:04:14",
"headers" : {
"b3" : "AntiSerialize 1659578752",
"b4" : [ "AntiSerialize 1659578763", "AntiSerialize 1659578774" ],
"content" : 999
}
}
Assume TestJackson is third party class and can't modify.
AntiSerialize is also a private nested class.

Custom Marshalling of HashMap<String,Object> in JAXB

I have a small problem regarding the marshalling via JAXB.
Currently I have a HashMap of Objects
#XmlJavaTypeAdapter(HashMapAdapter.class)
private Map<String, Object> data;
beeing marshalled by the Custom HashMapAdapter
public class HashMapAdapter extends XmlAdapter<HashMapAdapter.AdaptedMap,
Map<String, Object>> {
#XmlRootElement
public static class AdaptedMap {
#XmlVariableNode("key")
List<Data> entries = new ArrayList<>();
}
public static class Data {
#XmlTransient
public String key;
#XmlValue
public Object value;
}
#Override
public Map<String, Object> unmarshal(AdaptedMap v) throws Exception {
throw new NotImplementedException();
}
#Override
public AdaptedMap marshal(Map<String, Object> map) throws Exception {
AdaptedMap adaptedMap = new AdaptedMap();
for (Map.Entry<String, Object> entry : map.entrySet()) {
Data data = new Data();
data.key = entry.getKey();
data.value = entry.getValue();
adaptedMap.entries.add(data);
}
return adaptedMap;
}
}
The Marshalling is based on the following Post: http://blog.bdoughan.com/2013/06/moxys-xmlvariablenode-using-maps-key-as.html
The HashMap is filled with either Boolean, Long or String Values.
So regarding the Blog the expected JSON Output should be:
"data": {
"booleanValue": true,
"stringValue": "test",
"longValue": 1234
}
But the real outcome is:
"data": {
"longValue": {
"type": "long",
"value": 1234
},
"stringValue": {
"type": "string",
"value": "test"
},
"booleanValue": {
"type": "boolean",
"value": true
}
}
Im running on Payara Micro 174 and therefore on MOXy as JAXB provider.
Is it possible to get rid of the "type"/"value" nesting?
Best Regards
Simon
I do not have an MOxY implementation handy, could you try this and tell me if it works ?
public static class Data {
#XmlTransient
public String key;
#XmlElements({
#XmlElement(type=Long.class),
#XmlElement(type=String.class),
#XmlElement(type=Boolean.class)
})
#XmlPath(".")
public Object value;
}
EDIT :
The Output you get when using this Approach is:
"data": {
"stringValue": {
"value": test
},
"booleanValue": {
"value": true
},
"longValue": {
"value": 1234
}
}
Sadly this differs a little from the expected.

Java | Validating JSON objects inside wrapper

I am working with Java Spring framework and receive a JSON on input. It consists of an array of thousands of MyObjects. I would like the application to throw exception if one of the MyObjects or AnotherObjects fails a constraint (#NotNull or #Size).
So far, the application throws NullPointerException once it is accessing the null field when doing something with the object - not during its construction.
My question is:
Is there a way to check the constraints of nested JSON objects preferrably with annotation?
The JSON looks like:
{
"myObjects": [
{
"code": "PQ",
"another_objects": [
{
"attr1": "value1",
"attr2": "value2",
"attrN": "valueN"
},
{
"attr1": "value1",
"attr2": "value2",
"attrN": "valueN"
}
]
},
{
...
}
]
}
The servlet looks like:
#RequestMapping(value = ...)
public final void doSomething(#Valid #RequestBody MyObjectWrapper wrapper) {
// do something very time-heavy here
}
The objects are defined as follows:
public class MyObjectWrapper {
private List<MyObject> myObjects;
public List<MyObject> getMyObjects() {
return myObjects;
}
public void setMyObjects(List<MyObjects> myObjects) {
this.myObjects = myObjects;
}
}
And the MyObject class:
public class MyObject {
#NotNull
#NotEmpty(message = ...)
List<AnotherObject> anotherObjects;
#NotNull
#Size(min = 2, max = 2, message = ...)
String code;
#JsonCreator
public MyObject(#JsonProperty("another_objects") List<AnotherObjects> anotherObjects,
#JsonProperty("code") String code) {
this.code = code;
this.anotherObjects = anotherObjects;
}
/* getters and setters */
}
The AnotherObjects is similar but consists of Strings only.
Add #Valid to the nested object of the wrapper
public class MyObjectWrapper {
#Valid
private List<MyObject> myObjects;
public List<MyObject> getMyObjects() {
return myObjects;
}
public void setMyObjects(List<MyObjects> myObjects) {
this.myObjects = myObjects;
}
}

Implementation of Comparable not sorting List

I'm having trouble getting my objects to sort.
Some initial knowledge:
I'm using MongoDB to store my collection, I'm able to retrieve them and get everything back correctly.
I have a class that implements Comparable, with a compareTo function, but I also want to be able to sort on different properties, thus I've added static comparables for each property I want to sort on.
public class PlaceHolder implements Comparable<PlaceHolder>{
private String name;
private String icon;
private String originalLangcode;
//Getters and setters + constructors here, these work 100%.
#Override
public int compareTo(PlaceHolder ph) {
return this.getName().compareTo(ph.getName());
}
public static Comparator<PlaceHolder> nameComparator = new Comparator<PlaceHolder>() {
#Override
public int compare(PlaceHolder ph1, PlaceHolder ph2) {
return ph1.getName().compareTo(ph2.getName());
}
};
public static Comparator<PlaceHolder> iconComparator = new Comparator<PlaceHolder>() {
#Override
public int compare(PlaceHolder ph1, PlaceHolder ph2) {
return ph1.getIcon().compareTo(ph2.getIcon());
}
};
public static Comparator<PlaceHolder> nativeLangComparator = new Comparator<PlaceHolder>() {
#Override
public int compare(PlaceHolder ph1, PlaceHolder ph2) {
return ph1.getNativeLang().compareTo(ph2.getNativeLang());
}
};
}
I've then wrote a function that gets all placeholders from my mongodb, returning them in a list with PlaceHolder objects.
public List<PlaceHolder> getAllPlaceholders(String sortType) {
List<PlaceHolder> list = getPlaceholderList();
switch(sortType) {
case "name":
Collections.sort(list, PlaceHolder.nameComparator);
break;
case "icon":
Collections.sort(list, PlaceHolder.iconComparator);
break;
case "native":
Collections.sort(list, PlaceHolder.nativeLangComparator);
break;
default:
Collections.sort(list, PlaceHolder.nameComparator);
break;
}
return list;
}
I always get my data unsorted:
{ "_id" : { "$oid" : "56653f82972552a4024814a3"} , "name" : "testHolder" , "icon" : "archive" , "originalLangcode" : "ENG"}
{ "_id" : { "$oid" : "5665427a97253f798067c57b"} , "name" : "doItHolder" , "icon" : "car" , "originalLangcode" : "ENG"}
{ "_id" : { "$oid" : "566545dd9725050a53b4a5a8"} , "name" : "testableHolder" , "icon" : "adjust" , "originalLangcode" : "ENG"}
{ "_id" : { "$oid" : "5665479b972511264f55aae1"} , "name" : "dataHolder" , "icon" : "hdd-o" , "originalLangcode" : "ENG"}
I'm failing to see what goes wrong.
I've debugged the comparables, and they seem to work, returning negatives and positives. But the list just doesnt seem to get sorted.
I'm using getAllPlaceholders in my controller, passing it to my Page handler which in turn generates html for a table view.
public class PlaceHolderControllerF extends ControllerAbF {
#Autowired PlaceHolderRepo pr;
#RequestMapping(value = "placeholderlist", method = RequestMethod.GET)
#ResponseBody
public String editLanguage(HttpSession httpSession) {
if (getUser(httpSession) == null) {
return noPermission();
}
return Pages.getPlaceHolderList(pr.getAllPlaceholders("name"));
}
Pages just gets my index html, passes some variables to it that then runs through my templater which fills in the variables into the html.

Need help building JSONObject java

I'm trying to build the following json object in java dynamically, for example, if the WARN object doesn't exist, add it or any other one followed by adding a new label message object to the sub array.
This is an example of what I'm trying to dynamically build.
{
"warnings" : [
{
"WARN" : [
{
"label" : "Label Goes Here",
"message" : "Message Goes Here"
},
{
"label" : "Label Goes Here2",
"message" : "Message Goes Here2"
}
],"title" : "Please review the following warnings"
},
{
"NOTIFICATION" : [
{
"label" : "Label Goes Here3",
"message" : "Message Goes Here3"
},
{
"label" : "Label Goes Here4",
"message" : "Message Goes Here4"
}
],"title" : "Please review the following warnings"
}
]
}
This is the what I've tried.
public class Warning {
warningTypes = new JSONObject();
}
private JSONObject warningTypes;
public Warning() {
public Warning(WarningType warningType, String label, String message) {
this.warningType = warningType;
this.label = label;
this.message = message;
}
public void add(WarningType warningType, String label, String message) {
addToJSON(warningType, new JSONObject("label",label,"message",message));
}
private void addToJSON(WarningType warningType, JSONObject jsonObj) {
if(warningTypes.has(warningType.name())) {
JSONArray array = warningTypes.getJSONArray(warningType.name());
array.put(jsonObj);
} else {
warningTypes.put(warningType.name(), new JSONArray(jsonObj));
}
}
public JSONObject toJSON() {
return new JSONObject("warnings", new JSONArray(warningTypes));
}
}
However this is my outcome which you can see is incorrect. I'm unable to add the title do to the fact my warningTypes are being but into a single object.
{
"warnings" : [
{
"WARN" : [
{
"label" : "Label Goes Here",
"message" : "Message Goes Here"
},
{
"label" : "Label Goes Here2",
"message" : "Message Goes Here2"
}
],
"NOTIFICATION" : [
{
"label" : "Label Goes Here3",
"message" : "Message Goes Here3"
},
{
"label" : "Label Goes Here4",
"message" : "Message Goes Here4"
}
]
}
]
}
I can not figure out how to build this object dynamically, any help would be appreciated.
The JSON you are trying to create does not have the same key. Following code will give you the desired output. Refactor the parts into methods as necessary.
Code:
public static class Message {
private String label;
private String message;
public String getMessage() {
return message;
}
public String getLabel() {
return label;
}
public void setMessage(String message) {
this.message = message;
}
public void setLabel(String label) {
this.label = label;
}
}
public static enum WarningType {
WARN, NOTIFICATION
}
public static class Warning {
WarningType type;
List<Message> messages;
String title;
public WarningType getType() {
return type;
}
public void setType(WarningType type) {
this.type = type;
}
public void setMessages(List<Message> messages) {
this.messages = messages;
}
public void setTitle(String title) {
this.title = title;
}
public List<Message> getMessages() {
return messages;
}
public String getTitle() {
return title;
}
}
public static class Warnings {
List<Map<String, Object>> warnings;
public List<Map<String, Object>> getWarnings() {
return warnings;
}
public void setWarnings(List<Map<String, Object>> warnings) {
this.warnings = warnings;
}
public void setWarningsInMap(List<Warning> warningList) {
warnings = new ArrayList<>();
for(Warning each : warningList) {
Map<String, Object> m = new LinkedHashMap<>();
m.put(each.getType().name(), each.getMessages());
m.put("title", each.getTitle());
warnings.add(m);
}
}
}
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
List<Warning> warningList = new ArrayList<>();
Warning warn = new Warning();
warn.setType(WarningType.WARN);
List<Message> warnMessages = new ArrayList<>();
Message m = new Message();
m.setLabel("Label Goes Here");
m.setMessage("Message Goes Here");
warnMessages.add(m);
m = new Message();
m.setLabel("Label Goes Here2");
m.setMessage("Message Goes Here2");
warnMessages.add(m);
warn.setMessages(warnMessages);
warn.setTitle("Please review the following warnings");
warningList.add(warn);
Warning notification = new Warning();
notification.setType(WarningType.NOTIFICATION);
List<Message> notificationMessages = new ArrayList<>();
m = new Message();
m.setLabel("Label Goes Here3");
m.setMessage("Message Goes Here3");
notificationMessages.add(m);
m = new Message();
m.setLabel("Label Goes Here4");
m.setMessage("Message Goes Here4");
notificationMessages.add(m);
notification.setMessages(notificationMessages);
notification.setTitle("Please review the following warnings");
warningList.add(notification);
Warnings w = new Warnings();
w.setWarningsInMap(warningList);
String s = new ObjectMapper().defaultPrettyPrintingWriter().writeValueAsString(w);
System.out.println(s);
}
Output:
{
"warnings" : [ {
"WARN" : [ {
"message" : "Message Goes Here",
"label" : "Label Goes Here"
}, {
"message" : "Message Goes Here2",
"label" : "Label Goes Here2"
} ],
"title" : "Please review the following warnings"
}, {
"NOTIFICATION" : [ {
"message" : "Message Goes Here3",
"label" : "Label Goes Here3"
}, {
"message" : "Message Goes Here4",
"label" : "Label Goes Here4"
} ],
"title" : "Please review the following warnings"
} ]
}
Here's the link to a comprehensive guide.
If you want less work, you can use Google GSON to serialize POJOs to JSON objects. GSON can serialize POJOs, Arrays, Lists, Maps, other collections and will automatically differentiate between numbers and strings. This allows for better object oriented designs and lets you defer serializing to JSON objects up to the GSON. You don't have to worry about dynamically modifying JSONs, just dynamically modify the POJOs. Say you have some objects:
public class Warnings {
#SerializedName("WARN")
private Warn[] warns;
#SerializedName("NOTIFICATION")
private Notification[] notifications;
public WARN[] getWARN(){...}
public void setWarn(WARN[] warns){...}
...
}
public class Warn {
private String message;
private String label;
... // setters & getters
}
public class Notification {
private String message;
private String label;
... // setters & getters
}
you get the point
Then you can serialize it so:
Warnings someWarnings = new Warnings();
// Populate warnings
Gson gson = new Gson();
// Serialize
String jsonString = gson.toJson(someWarnings);
// Deserialize
Warnings sameWarinings = gson.fromJson(jsonString);

Categories

Resources