JSON
{
"type": {
"type1": {
"sub1": [
"sub1A": {
}
]
}
}
"type2": {
"type1": {
"sub1": [
"sub1A": {
}
]
}
}
}
I have Json like this , I am not getting how to create map for this, like
Map<String, Object> requestMap = new HashMap<>();
requestMap.get("type");
Inside type again create map containing sub data.
I didn't understand your question completely. But, from your statement, if you are trying to fetch json data into your class and that JSON data has recursive mapping, i.e., one object contains itself under its body. Then, I don't think you need HashMap to map this json into your class. You can use single Entity with a field of type of itself like employee-manager relationship. e.g.,
class Type {
private Type type;
private String otherField;
// constructors, setters, getters
}
Now, you can read values from type using the recursive functions.
According to what you have shared you can use:
Map <String, Map<String, Map<String, Set<Map<String, String>>>>>
Or to get more details concerning working with json objects you can look at: Query a JSONObject in java
Related
I have a Bean class which I have constructed according to the response I have got. Below is my Postman response.
{
"EU": [
{
"calId": "EU",
"calDate": "2022-11-01",
"prevBusinessDay": "2022-11-01",
"nextBusinessDay": "2022-11-01",
"businessDay": true,
"monthEndBusinessDay": false
}
],
"AU": [
{
"calId": "AU",
"calDate": "2022-11-01",
"prevBusinessDay": "2022-11-01",
"nextBusinessDay": "2022-11-01",
"businessDay": true,
"monthEndBusinessDay": false
}
]
}
According to this I have constructed the Bean as follows
#Data
#AllArgsConstructor
#NoArgsConstructor
public class IndexCalendarDateResponseBean {
#JsonProperty("EU")
private List<IndexCalendarDateResponseWrapper> EU;
#JsonProperty("AU")
private List<IndexCalendarDateResponseWrapper> AU;
}
IndexCalendarDateResponseWrapper is another POJO which contains all the variables calId,calDate etc which are inside the two JSON arrays.
My issue here is every time this array names cannot be EU and AU. This depends on whatever I pass in the API request URI path parameters. If I pass something else, then that will be the JSON array variable names. So my bean will not work for that. So I wanted to construct a bean that should be dynamic in nature. What I am doing with this bean is I am using this bean to fetch an API response as follows.
IndexCalendarDateResponseBean actualRIOutput = JsonPath.from(response.extract().asInputStream()).getObject("", IndexCalendarDateResponseBean.class);
So how to construct a bean such that even if I pass say something else as path parameters say JU, BU, and SU or JU and BU anything, it will fetch the response for me? Also the JSON array variables I am passing in path parameters can also vary in quantity i.e. it can be two, three or any number of paramaters I can pass. So that also the bean should accept? Is any other bean needed for this idea to support?
So how to construct a bean such that even if I pass say something else as path parameters say JU, BU, and SU or JU and BU anything, it will fetch the response for me?
You can create a field of type Map<String, List<IndexCalendarDateResponseWrapper>> and make use of the method annotated with #JsonAnySetter to populate it.
If you need the ability to serialize this POJO into the same JSON, then you can add another method exposing the data of the Map annotated with #JsonAnyGetter.
public class IndexCalendarDateResponseBean {
private Map<String, List<IndexCalendarDateResponseWrapper>> responseWrappers = new HashMap<>();
#JsonAnySetter
public void readResponseWrappers(String key, List<IndexCalendarDateResponseWrapper> value) {
responseWrappers.put(key,value);
}
#JsonAnyGetter
public Map<String, List<IndexCalendarDateResponseWrapper>> writeResponseWrappers() {
return responseWrappers;
}
}
Also the JSON array variables I am passing in path parameters can also vary in quantity i.e. it can be two, three or any number of paramaters
When you're deserializing JSON into a POJO missing properties would be set to default values of the corresponding type (for instance, null for reference types).
When you're serializing a POJO into JSON if you don't want to include null fields you can make use of the annotation #JsonInclude providing the value of JsonInclude.Include.NON_NULL or JsonInclude.Include.NON_EMPTY (depending on your requirements).
Example:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class IndexCalendarDateResponseWrapper {
// field, constructors, etc.
}
Why not replace IndexCalendarDateResponseBean with Map<Stirg,List< IndexCalendarDateResponseWrapper>>。
private static ObjectMapper mapper = new ObjectMapper();
static {
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
}
public static <T> T parseObject(String json, TypeReference<T> typeRef) throws Exception {
return mapper.readValue(json, typeRef);
}
....
return JsonUtils.parseObject(jsonStr, new TypeReference<Map<String, List<IndexCalendarDateResponseWrapper>>>() {
});
Please I'm doing a practice with MongoDB and Spring but I have the following dilemma; in the DB I have the following information:
[
{
"menu":"querys",
"optionsList":[
{
"options":[
"0001",
"0022",
"0014",
"0041",
"0042",
"0043"
]
}
]
},{..},{...}
]
The structure of the object in Java is as follows:
#Document(collection = "menu")
public class GetAllRules implements Serializable {
private static final long serialVersionUID = 7375796948388333325L;
#JsonProperty(value = "menu")
private String name;
#JsonProperty(value = "optionsList")
private List<Map<String, ?>> optionsList;
//Getters and Setters.
With the following method I get the JSON (I'm using the MongoRepository), with the first FOR I get all the information and with the second I get the inner map, but I don't know how to iterate the information found inside options, I would appreciate it if someone can you help me with the issue:
#GetMapping("/final")
public String obtener() {
List<GetAllRules> allRules = iGetMenuService.getAll(); // Mongo List
String key= "options";
for (GetAllRules rules : allRules) {
for (Map<String, ?> internal : rules.getOptionsList()) {
System.out.println(internal.get(key));
}
}
return "finalizado";
}
With line System.out.println(internal.get(key)); I get the key values that I need but now I don't know how to go through it one by one to do something specific with each data.
[0001, 0022, 0014, 0041, 0042, 0043]
[0238]
[1001, 1003]
[0108, 0109, 0102]
[0601, 0602, 0604, 0604]
[0603, 0901, 0901]
[0238]
[0001]
Thanks.
how to iterate the information found inside options
Your options field is just another array/list of strings, so you can specify that in your pojo:
#JsonProperty(value = "optionsList")
private List<Map<String, List<String>>> optionsList;
with that, you can add one more iteration
for (GetAllRules rules : allRules) {
for (Map<String, List<String>> internal : rules.getOptionsList()) {
for (String value : internal.get(key)) {
System.out.println(value);
// will print "0001", ...
}
}
}
Nicer way to handle this would be to use Java streams - you don't want to use too many nested loops,
it may look like this:
allRules.stream()
.map(GetAllRules::getOptionsList)
.flatMap(Collection::stream)
.flatMap(option -> option.get(key).stream())
.forEach(System.out::println);
I want to use Jackson to implement toString() to return the JSON representation of an object, but I do not want to use any Jackson annotation in my code.
I tried an implementation along the lines of:
public String toString()
{
Map<String,Object> ordered = ImmutableMap.<String, Object>builder().
put("createdAt", createdAt.toString()).
put("address", address.toString()).
build();
ObjectMapper om = new ObjectMapper();
om.enable(SerializationFeature.INDENT_OUTPUT);
try
{
return om.writeValueAsString(object);
}
catch (JsonProcessingException e)
{
// Unexpected
throw new AssertionError(e);
}
}
This works well for simple fields but if "address" has its own fields then instead of getting this:
{
"address" : {
"value" : "AZ4RPBb1kSkH4RNewi4NXNkBu7BX9DmecJ",
"tag" : null
}
I get this output instead:
{
"address" : "{\n\"value\" : \"AZ4RPBb1kSkH4RNewi4NXNkBu7BX9DmecJ\",\n \"tag\" : null"
}
In other words, the address value is being treated like a String as opposed to a JsonNode.
To clarify:
On the one hand, I want to control how simple class fields are converted to String. I don't want to use Jackson's built-in converter.
On the other hand, for complex fields, returning a String value to Jackson leads to the wrong behavior.
I believe that I could solve this problem by adding a public toJson() method to all my classes. That method would return a Map<String, JsonNode>, where the value is a string node for simple fields and the output of toJson() for complex fields. Unfortunately, this would pollute my public API with implementation details.
How can I achieve the desired behavior without polluting the class's public API?
UPDATE: I just saw an interesting answer at https://stackoverflow.com/a/9599585/14731 ... Perhaps I could convert the String value of complex fields back to JsonNode before passing them on to Jackson.
I think you should implement two methods in each class - one to dump data, second to build JSON out of raw data structure. You need to separate this, otherwise you will nest it deeper and deeper every time you encapsulate nested toString() calls.
An example:
class Address {
private BigDecimal yourField;
/* …cut… */
public Map<String, Object> toMap() {
Map<String, Object> raw = new HashMap<>();
raw.put("yourField", this.yourField.toPlainString());
/* more fields */
return raw;
}
#Override
public String toString() {
// add JSON processing exception handling, dropped for readability
return new ObjectMapper().writeValueAsString(this.toMap());
}
}
class Employee {
private Address address;
/* …cut… */
public Map<String, Object> toMap() {
Map<String, Object> raw = new HashMap<>();
raw.put("address", this.address.toMap());
/* more fields */
return raw;
}
#Override
public String toString() {
// add JSON processing exception handling, dropped for readability
return new ObjectMapper().writeValueAsString(this.toMap());
}
}
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> {
}
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.