I want to map an object to json to look like this:
{"MyClass" :
[
{"key" : "value"},
{"key" : "value"}
]}
However my code is giving me:
{"MyClass" : {
"list" : {
"key" : "value",
"key" : "value"
}
And my code is like this:
public class MyClass {
private List<Map<String,String>> list;
//getters & setters......
}
And:
Map<String, String> map1 = new HashMap<String,String>();
map1.put("key", "value");
Map<String,String> map2 = new HashMap<String,String>();
map2.put("key","value");
List<Map<String,String> list = new ArrayList<Map<String,String>();
list.add(map1);
list.add(map2);
And I am using ObjectMapper to map the values. What can I do to get the layout I want?
using your code
class MyClass {
#JsonProperty("MyClass")
public List<Map<String,String>> list;
public static void main(String[] args) throws JsonProcessingException {
Map<String, String> map1 = new HashMap<String,String>();
map1.put("key", "value");
Map<String,String> map2 = new HashMap<String,String>();
map2.put("key","value");
List<Map<String,String>> list = new ArrayList<Map<String,String>>();
list.add(map1);
list.add(map2);
MyClass d = new MyClass();
d.list = list;
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(d);
System.out.println(json);
}
}
output
{"MyClass":[{"key":"value"},{"key":"value"}]}
I used #JsonProperty to change the name but you can also change the name of the var from list to whatever
Note: this is just draft implementation... don't use public class vars, and such...
Related
I am trying to do the following
Given
Map<String, String> labels = {"en_GB" : "English", "de" : "German", "it" : "Italian"}....
I would like to use Jackson to serialize it to
[{"language" : "en_GB", "label" : "English"}, {"language" : "de", "label" : "German"}, {"language" : "it", "label" : "Italian"}]
Essentially splitting the map into arrays of objects, with the key and value as separate properties
Instead of
{"en_GB" : "English", "de" : "German", "it" : "Italian"}
I have searched the entirety of Jackson docs and i cannot find an answer to this. I would appreciate some help. Thanks in advance!
Method 1: define a middle POJO and convert map<String,String> to list of middle object
first to define a middle POJO:
public class LanguageInfo {
private String language;
private String label;
}
convert map to list of middle POJO and serialize list to string:
Map<String, String> labels = new HashMap<>();
labels.put("en_GB", "English");
labels.put("de", "German");
labels.put("it", "Italian");
List<LanguageInfo> languageInfoList = labels.entrySet().stream()
.map(entry -> {
LanguageInfo info = new LanguageInfo();
info.setLabel(entry.getValue());
info.setLanguage(entry.getKey());
return info;
}).collect(Collectors.toList());
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(languageInfoList));
output is:
[
{
"language":"de",
"label":"German"
},
{
"language":"en_GB",
"label":"English"
},
{
"language":"it",
"label":"Italian"
}
]
Method 2: define a JsonSerializer and use this to do serialize work
Assume you want to directly serialize the below class:
#Data
public class LanguageInfos {
private Map<String, String> labels;
}
First,define a JsonSerializer to serialize LanguageInfos:
need middle POJO LanguageInfo defined above:
public class LanguageInfosJsonSerializer extends JsonSerializer<LanguageInfos> {
#Override
public void serialize(LanguageInfos languageInfos, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
// LanguageInfo defined in below code
List<LanguageInfo> languageInfoList = languageInfos.getLabels().entrySet().stream()
.map(entry -> {
LanguageInfo info = new LanguageInfo();
info.setLabel(entry.getValue());
info.setLanguage(entry.getKey());
return info;
}).collect(Collectors.toList());
jsonGenerator.writeObject(languageInfoList);
}
}
no need middle POJO:
public class LanguageInfosJsonSerializer extends JsonSerializer<LanguageInfos> {
#Override
public void serialize(LanguageInfos languageInfos, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartArray(languageInfos.getLabels().size());
languageInfos.getLabels().entrySet().forEach(new Consumer<Map.Entry<String, String>>() {
#SneakyThrows
#Override
public void accept(Map.Entry<String, String> entry) {
ObjectNode node = new ObjectMapper().createObjectNode();
node.put("language",entry.getKey());
node.put("label",entry.getValue());
jsonGenerator.writeObject(node);
}
});
jsonGenerator.writeEndArray();
}
}
Second,register this Serializer to a ObjectMapper object:
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(LanguageInfos.class, new LanguageInfosJsonSerializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(simpleModule);
Third,use the ObjectMapper serialize directly:
Map<String, String> labels = new HashMap<>();
labels.put("en_GB", "English");
labels.put("de", "German");
labels.put("it", "Italian");
LanguageInfos languageInfos = new LanguageInfos();
languageInfos.setLabels(labels);
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(LanguageInfos.class, new LanguageInfosJsonSerializer());
mapper.registerModule(simpleModule);
System.out.println(mapper.writeValueAsString(languageInfos));
output is:
[
{
"language":"de",
"label":"German"
},
{
"language":"en_GB",
"label":"English"
},
{
"language":"it",
"label":"Italian"
}
]
code below return me list of two objects like this:
[
{
value: "ssss",
},
{
value: "ssss",
},
]
but i want to return list like this:
{
value1:"xxxx",
value2:"xxxx"
}
here is my code
#Override
public List<OrderModel> getOrderByCode(UserEntity user) throws ErrorException {
List<UserEntity> userOrder = userRepository.findByUserAndOrderCodeIn(user, Arrays.asList(OrderCodes.OK, OrderCodes.DONE));
List<OrderModel> om = new ArrayList<OrderModel>();
for(UserAnswerEntity userAnswerEntity : userAnswers){
OrderModel orderModel = new OrderModel();
orderModel.setValue(userAnswerEntity.getValue());
om.add(orderModel );
}
return om;
}
public class OrderModel{
String value;
//get,set
}
can someon tell me how can i return only list like above?
You are trying to make a custom response. One of the possible way is this
Map<String,String> map = new HashMap<String,String>();
map.put("key1", "value1");
map.put("key2", "value2");
System.out.println(map.toString().replace("{", "[").replace("}", "]"));
Change the return type of getOrderByCode() to string and convert your List object to JSON string.
private static final ObjectMapper jsonMapper = new ObjectMapper().setSerializationInclusion(Include.NON_NULL);
return jsonMapper.writeValueAsString(om);
for you example, you can use Map instead of List
try this:
AtomicInteger index = new AtomicInteger()
Function<UserAnswerEntity, String> createIndex = (userAnswer) -> "value" + index.getAndIncrement();
return userAnswers.stream().collect(Collectors.toMap(createIndex, UserAnswerEntity::getValue));
I have an enhanced question regarding Flatten a JSON string to Map using Gson or Jackson.
My scenario included duplicated keys, so the solution in the above question will cause some duplicated keys overwritten. So I am thinking to construct keys by combining each level's key together.
So how to achieve that?
For example:
{
"id" : "123",
"name" : "Tom",
"class" : {
"subject" : "Math",
"teacher" : "Jack"
}
}
I want to get the Map:
"id" : "123",
"name" : "Tom",
"class.subject" : "Math",
"class.teacher" : "Jack"
************************Update Solution*************************************
Based on #Manos Nikolaidis's answer, I am able to achieve the following solution by considering ArrayNode.
public void processJsonString(String jsonString) throws Exception {
ObjectMapper mapper = new ObjectMapper();
ArrayNode arrayNode = (ArrayNode) mapper.readTree(jsonString);
processArrayNode(arrayNode);
}
private void processObjectNode(JsonNode jsonNode) {
Map<String, String> result = new HashMap<>();
Iterator<Map.Entry<String, JsonNode>> iterator = jsonNode.fields();
iterator.forEachRemaining(node -> mapAppender(result, node, new ArrayList<String>()));
}
private void processArrayNode(ArrayNode arrayNode) {
for (JsonNode jsonNode : arrayNode) {
processObjectNode(jsonNode);
}
}
private void mapAppender(Map<String, String> result, Map.Entry<String, JsonNode> node, List<String> names) {
names.add(node.getKey());
if (node.getValue().isTextual()) {
String name = names.stream().collect(Collectors.joining("."));
result.put(name, node.getValue().asText());
} else if (node.getValue().isArray()) {
processArrayNode((ArrayNode) node.getValue());
} else if (node.getValue().isNull()) {
String name = names.stream().collect(Collectors.joining("."));
result.put(name, null);
} else {
node.getValue().fields()
.forEachRemaining(nested -> mapAppender(result, nested, new ArrayList<>(names)));
}
}
You can get the JSON as JsonNode and go through all fields recursively and add key and value field to a Map. When a value is an object instead of string you can add the field name to List to be joined with periods when a string is finally encountered. First create (for readability) a separate method that add Json fields to a Map:
void mapAppender(Map<String, String> result, Entry<String, JsonNode> node, List<String> names) {
names.add(node.getKey());
if (node.getValue().isTextual()) {
String name = names.stream().collect(joining("."));
result.put(name, node.getValue().asText());
} else {
node.getValue().fields()
.forEachRemaining(nested -> mapAppender(result, nested, new ArrayList<>(names)));
}
}
and use it like this:
ObjectMapper mapper = new ObjectMapper();
Map<String, String> result = new HashMap<>();
mapper.readTree(json).fields()
.forEachRemaining(node -> mapAppender(result, node, new ArrayList<String>()));
Where fields() returns an Iterator. Beware of StackOverflowErrors and perhaps low performance for deeply nested JSON.
I resolved this using below simple code, Only think is need to download jettison and flattener.JsonFlattener library
import java.util.Map;
import org.codehaus.jettison.json.JSONObject;
import com.github.wnameless.json.flattener.JsonFlattener;
public class test {
public static void main(String[] args) {
String jsonString = "{\"id\" : \"123\",\"name\" : \"Tom\",\"class\" : {\"subject\" : \"Math\",\"teacher\" : \"Jack\"}}";
JSONObject jsonObject = new JSONObject();
String flattenedJson = JsonFlattener.flatten(jsonString);
Map<String, Object> flattenedJsonMap = JsonFlattener.flattenAsMap(jsonString);
System.out.println(flattenedJsonMap);
}
}
Reference link : https://github.com/wnameless/json-flattener
I have an enhanced question regarding Flatten a JSON string to Map using Gson or Jackson.
My scenario included duplicated keys, so the solution in the above question will cause some duplicated keys overwritten. So I am thinking to construct keys by combining each level's key together.
So how to achieve that?
For example:
{
"id" : "123",
"name" : "Tom",
"class" : {
"subject" : "Math",
"teacher" : "Jack"
}
}
I want to get the Map:
"id" : "123",
"name" : "Tom",
"class.subject" : "Math",
"class.teacher" : "Jack"
************************Update Solution*************************************
Based on #Manos Nikolaidis's answer, I am able to achieve the following solution by considering ArrayNode.
public void processJsonString(String jsonString) throws Exception {
ObjectMapper mapper = new ObjectMapper();
ArrayNode arrayNode = (ArrayNode) mapper.readTree(jsonString);
processArrayNode(arrayNode);
}
private void processObjectNode(JsonNode jsonNode) {
Map<String, String> result = new HashMap<>();
Iterator<Map.Entry<String, JsonNode>> iterator = jsonNode.fields();
iterator.forEachRemaining(node -> mapAppender(result, node, new ArrayList<String>()));
}
private void processArrayNode(ArrayNode arrayNode) {
for (JsonNode jsonNode : arrayNode) {
processObjectNode(jsonNode);
}
}
private void mapAppender(Map<String, String> result, Map.Entry<String, JsonNode> node, List<String> names) {
names.add(node.getKey());
if (node.getValue().isTextual()) {
String name = names.stream().collect(Collectors.joining("."));
result.put(name, node.getValue().asText());
} else if (node.getValue().isArray()) {
processArrayNode((ArrayNode) node.getValue());
} else if (node.getValue().isNull()) {
String name = names.stream().collect(Collectors.joining("."));
result.put(name, null);
} else {
node.getValue().fields()
.forEachRemaining(nested -> mapAppender(result, nested, new ArrayList<>(names)));
}
}
You can get the JSON as JsonNode and go through all fields recursively and add key and value field to a Map. When a value is an object instead of string you can add the field name to List to be joined with periods when a string is finally encountered. First create (for readability) a separate method that add Json fields to a Map:
void mapAppender(Map<String, String> result, Entry<String, JsonNode> node, List<String> names) {
names.add(node.getKey());
if (node.getValue().isTextual()) {
String name = names.stream().collect(joining("."));
result.put(name, node.getValue().asText());
} else {
node.getValue().fields()
.forEachRemaining(nested -> mapAppender(result, nested, new ArrayList<>(names)));
}
}
and use it like this:
ObjectMapper mapper = new ObjectMapper();
Map<String, String> result = new HashMap<>();
mapper.readTree(json).fields()
.forEachRemaining(node -> mapAppender(result, node, new ArrayList<String>()));
Where fields() returns an Iterator. Beware of StackOverflowErrors and perhaps low performance for deeply nested JSON.
I resolved this using below simple code, Only think is need to download jettison and flattener.JsonFlattener library
import java.util.Map;
import org.codehaus.jettison.json.JSONObject;
import com.github.wnameless.json.flattener.JsonFlattener;
public class test {
public static void main(String[] args) {
String jsonString = "{\"id\" : \"123\",\"name\" : \"Tom\",\"class\" : {\"subject\" : \"Math\",\"teacher\" : \"Jack\"}}";
JSONObject jsonObject = new JSONObject();
String flattenedJson = JsonFlattener.flatten(jsonString);
Map<String, Object> flattenedJsonMap = JsonFlattener.flattenAsMap(jsonString);
System.out.println(flattenedJsonMap);
}
}
Reference link : https://github.com/wnameless/json-flattener
I'm using a Jackson library to parse JSON:
{
"employees": [
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName":"Jones" }
]
}
Here is what I'm doing:
public void testJackson() throws IOException {
JsonFactory factory = new JsonFactory();
ObjectMapper mapper = new ObjectMapper(factory);
File from = new File("emp.txt"); // JSON object comes from
TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() {};
HashMap<String, Object> o = mapper.readValue(from, typeRef);
Employees employees = new Employees();
employees.employees = (List<Employer>)o.get("employees"); // retrieving list of Employer(s)
employees.showEmployer(1); // choose second to print out to console
System.out.println("Got " + o); // just result of file reading
}
public static class Employees {
public List<Employer> employees;
public void showEmployer(int i) {
System.out.println(employees.get(i));
}
}
public static class Employer {
public String firstName;
public String lastName;
}
The output I'm getting:
{firstName=Anna, lastName=Smith}
Got {employees=[{firstName=John,
lastName=Doe}, {firstName=Anna, lastName=Smith}, {firstName=Peter,
lastName=Jones}]}
But I'm not expecting the elements in my List to be HashMap instances, but Employer objects. This is what Jackson library is supposed to be, isn't it? Could you guys correct me where I am wrong?
I haven't used Jackson, but it seems you're getting what you asked for - a HashMap of String, Object pairs. Perhaps you need to be more explicit in the 'value' portion of the map? Since the value is an array of Employee objects, you might try:
TypeReference<HashMap<String, List<Employee>>> typeRef = new TypeReference<HashMap<String, List<Employee>>>() {};
HashMap<String, List<Employee>> o = mapper.readValue(from, typeRef);