Before this is marked as a duplicate please read the question (I did look at similar ones). Thank you.
For simplicity, assume I have JSON like this:
{
"clients" : [
{
"name" : "client 1",
"id" : 1
},
{
"name" : "client 2",
"id" : 2
}
],
"other" : {
"something" : ""
}
...
}
So I want to create a hash map of only the clients and their fields. The basic question is how would I go about doing this using Jackson methods for a single JSON array like clients? I've tried to look online but all of the examples that I have seen either don't use Jackson or only are for a single JSON object like so:
HashMap<String, String>[] values = new ObjectMapper().readValue(jsonString, new TypeReference<HashMap<String, String>[]>() {});
I've also seen Gson examples and I know I can do some string parsing magic:
jsonSting = jsonString.substring(jsonString.indexOf("["), (jsonString.indexOf("]")+1))
to get it in a format that I can use, but I want to try it with Jackson to avoid importing another library. Any ideas?
Rephrasing the question:
So if I only had a list of clients like so:
jsonString = [{"name" : "client 1","id" : 1},{"name" : "client 2","id" : 2}]
then I could just do:
HashMap[] values = new ObjectMapper().readValue(jsonString, new TypeReference[]>() {});
to get what I want. I am basically asking if there is a way using Jackson methods to get the jsonString above from the large JSON section on top. I know I can easily do it with this example with string parsing but there will be more complex situations in the future and string parsing is not really considered best practice
You can extract a part of the JSON tree using the Jackson tree model API and then convert it to an array of maps.
Here is an example:
public class JacksonReadPart {
public static final String JSON = "{\n" +
" \"clients\" : [\n" +
" {\n" +
" \"name\" : \"client 1\",\n" +
" \"id\" : 1\n" +
" },\n" +
" {\n" +
" \"name\" : \"client 2\",\n" +
" \"id\" : 2\n" +
" }\n" +
"],\n" +
" \"other\" : {\n" +
" \"something\" : \"\"\n" +
" }\n" +
"\n" +
"}";
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(JSON).path("clients");
// non type safe
Map<String, Object>[] clients = mapper.treeToValue(node, Map[].class);
System.out.println(Arrays.toString(clients));
// type safe
JsonParser parser = mapper.treeAsTokens(node);
clients = parser.readValueAs(new TypeReference<Map<String, Object>[]>() {});
System.out.println(Arrays.toString(clients));
}
}
Output:
[{name=client 1, id=1}, {name=client 2, id=2}]
[{name=client 1, id=1}, {name=client 2, id=2}]
Related
I am using Jackson's YAML parser and I want to parse a YAML file without having to manually create a Java class that matches the yaml file. All the examples I can find map it to an object such as here: https://www.baeldung.com/jackson-yaml
The yaml file that is given to me will not always be the same so I need to parse it during runtime, is it possible to achieve this with jackson-yaml?
If you don't know the exact format, you're going to have to parse the data to a tree and process it manually, which can be tedious. I'd use Optional for mapping and filtering.
Example:
public static final String YAML = "invoice: 34843\n"
+ "date : 2001-01-23\n"
+ "product:\n"
+ " - sku : BL394D\n"
+ " quantity : 4\n"
+ " description : Basketball\n"
+ " price : 450.00\n"
+ " - sku : BL4438H\n"
+ " quantity : 1\n"
+ " description : Super Hoop\n"
+ " price : 2392.00\n"
+ "tax : 251.42\n"
+ "total: 4443.52\n";
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
JsonNode jsonNode = objectMapper.readTree(YAML);
Optional.of(jsonNode)
.map(j -> j.get("product"))
.filter(ArrayNode.class::isInstance)
.map(ArrayNode.class::cast)
.ifPresent(projectArray -> projectArray.forEach(System.out::println));
}
Output:
{"sku":"BL394D","quantity":4,"description":"Basketball","price":450.0}
{"sku":"BL4438H","quantity":1,"description":"Super Hoop","price":2392.0}
Like when you are parsing JSON, you can parse into a Map:
Example
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
#SuppressWarnings("unchecked")
Map<String, Object> map = mapper.readValue(new File("test.yaml"), Map.class);
System.out.println(map);
test.yaml
orderNo: A001
date: 2019-04-17
customerName: Customer, Joe
orderLines:
- item: No. 9 Sprockets
quantity: 12
unitPrice: 1.23
- item: Widget (10mm)
quantity: 4
unitPrice: 3.45
Output
{orderNo=A001, date=2019-04-17, customerName=Customer, Joe, orderLines=[{item=No. 9 Sprockets, quantity=12, unitPrice=1.23}, {item=Widget (10mm), quantity=4, unitPrice=3.45}]}
I have a csv file with an initial data for my app.
{
"id": 1,
"topic": "Архитектура",
"question": "Как называется буддийское архитектурное культовое сооружение?",
"rightanswer": "Ступа",
"wronganswer1": "Баба",
"wronganswer2": "Яга",
"wronganswer3": "Метла",
"passed": false,
"right": false
},
I need to parse it to json with the array of "answers", what options i have ?
Result should be like that :
{
"id": 1,
"topic": "Архитектура",
"question": "Как называется буддийское архитектурное культовое сооружение?",
"answers":[
"Ступа",
"Баба",
"Яга",
"Метла" ],
"passed": false,
"right": false
}
You are almost in the right direction but you have to use JSONArray for answers rather then adding them directly into the object with the name.
you can have an if where you will check if key contains the answer string then you can add into the JSONArray else add that key and value into JSONObject and add this JSONArray with the key answers into the main object once you done by adding all field.
This logic will keep your logic flexible.
it will help you to achieve your desire JSON.
EDIT: I would suggest you change your excel structure if you can. you should have all possible options in a single column (such as opt1,opt2,opt3,opt4) and correct answers in another column to gain simplicity and flexibility in your excel designa nd code.
I'm using gson
String str = "{\r\n" +
" \"id\": 1,\r\n" +
" \"topic\": \"Архитектура\",\r\n" +
" \"question\": \"Как называется буддийское архитектурное культовое сооружение?\",\r\n" +
" \"rightanswer\": \"Ступа\",\r\n" +
" \"wronganswer1\": \"Баба\",\r\n" +
" \"wronganswer2\": \"Яга\",\r\n" +
" \"wronganswer3\": \"Метла\",\r\n" +
" \"passed\": false,\r\n" +
" \"right\": false\r\n" +
" }"; //<== your json input
JsonParser parser = new JsonParser();
JsonObject input = parser.parse(str).getAsJsonObject(); //parser you Json to object
JsonObject output = new JsonObject();//my new jsonOutput
output.add("id", input.get("id"));
//other field .....
//the trick start here
JsonArray answer = new JsonArray();
answer.add(input.get("rightanswer"));
answer.add(input.get("wronganswer1"));
answer.add(input.get("wronganswer2"));
answer.add(input.get("wronganswer3"));
output.add("answers", answer);
System.out.println(output.toString());
result
{"id":1,"answers":["Ступа","Баба","Яга","Метла"]} // to lazy to parse other field sorry
Hope it helps
I have the following JSON
{
"display": {
"icon": {
"item": "minecraft:elytra"
},
"title": "Learn to Fly"
},
"parent": "minecraft:story/enter_end_gateway",
"criteria": {
"elytra": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"item": "minecraft:elytra",
"data": 1
}
]
}
}
}
}
How would I be able to create this within Java?
Along with being able to get each element, eg the title.
Thanks!
check this library gson from google its easy to use and have a lot of APIs to work with JSON
The process you are asking about is called unmarshalling (parsing a serialised object in JSON/XML/other formats into an object to work with within the object-oriented context).
I would recommend having a look at Jackson. It's a popular library which is used in such the well-known frameworks like Spring (the MVC module).
Having created a domain object, you could transform a jsonData into an instance of the DomainClass class like
DomainClass instance = new ObjectMapper().readValue(jsonData, DomainClass.class);
The jsonData source can be Files, InputStreams, byte arrays, Strings and so on. It's up to you to pick up the most convenient way to obtain that data.
Along with being able to get each element, eg the title.
When the instance is ready, you are free to get elements (now fields) through the accessors (the get methods).
Here is the code to build your json with org.json library in java:
import org.json.JSONArray;
import org.json.JSONObject;
public class JSONBuilderMinecraft {
public static void main(String args[]) throws Exception{
JSONObject mainJson = new JSONObject();
//inner most json array
JSONArray itemarray=new JSONArray();
//inner most json
JSONObject itemsJson= new JSONObject();
itemsJson.put("item", "minecraft:elytra");
itemsJson.put("data", 1);
itemarray.put(itemsJson);
JSONObject conditions = new JSONObject();
conditions.put("items", itemarray);
JSONObject elytra = new JSONObject();
elytra.put("trigger", "minecraft:inventory_changed");
elytra.put("conditions", conditions);
mainJson.put("criteria", elytra);
mainJson.put("parent", "minecraft:story/enter_end_gateway");
JSONObject icon = new JSONObject();
icon.put("item", "minecraft:elytra");
JSONObject display = new JSONObject();
display.put("title", "Learn to Fly");
display.put("icon", icon);
mainJson.put("display", display);
System.out.println(mainJson.toString());
}
}
instead of creating your code like this stupid idea
JSONObject jsonObject=new JSONObject();
JSONObject jsonObject2=new JSONObject();
JSONObject jsonObject3=new JSONObject();
jsonObject.put("display",jsonObject3);
you can use Gson, so simple it creates json object without wasting your time on code make a class for each new Json object and make a relations between them.
Try this,it works fine:
public static void main(String[] args) throws JSONException {
String jsonString = "{\n"
+ " \"display\": {\n"
+ " \"icon\": {\n"
+ " \"item\": \"minecraft:elytra\"\n"
+ " },\n"
+ " \"title\": \"Learn to Fly\"\n"
+ " },\n"
+ " \"parent\": \"minecraft:story/enter_end_gateway\",\n"
+ " \"criteria\": {\n"
+ " \"elytra\": {\n"
+ " \"trigger\": \"minecraft:inventory_changed\",\n"
+ " \"conditions\": {\n"
+ " \"items\": [\n"
+ " {\n"
+ " \"item\": \"minecraft:elytra\",\n"
+ " \"data\": 1\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }";
JSONObject object = new JSONObject(jsonString);
JSONObject disp = object.getJSONObject("display");
String title = disp.getString("title");
System.out.println("title is " + title);
}
If we have given 2 Strings of type json, how can we merge them into single json String in java?
e.g.
String json1 = {
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S"
}
}
}
String json2 = {
"glossary": {
"title": "person name",
"age": "25"
}
}
Should produce
String mergedJson = {
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S"
},
"age": "25"
}
}
Below code should do it, with a couple of assumptions:
You are using ObjectMapper of Jackson library (com.fasterxml.jackson.databind.ObjectMapper) to serialise/deserialise json
fields of json1 will always overwrite json2 while merging
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map1 = mapper.readValue("json1", Map.class);
Map<String, Object> map2 = mapper.readValue("json2", Map.class);
Map<String, Object> merged = new HashMap<String, Object>(map2);
merged.putAll(map1);
System.out.println(mapper.writeValueAsString(merged));
Here is the code which recursively merges two jsons. This outputs as excepted:
NOTE: This is deep merge, not shallow merge ( similar concept used for shall vs deep copy)
private static JsonObject merge(JsonObject json1Obj, JsonObject json2Obj) {
Set<Entry<String, JsonElement>> entrySet1 = json1Obj.entrySet();
for (Entry<String, JsonElement> entry : entrySet1) {
String key1 = entry.getKey();
if (json2Obj.get(key1) != null) {
JsonElement tempEle2 = json2Obj.get(key1);
JsonElement tempEle1 = entry.getValue();
if (tempEle2.isJsonObject() && tempEle1.isJsonObject()) {
JsonObject mergedObj = merge(tempEle1.getAsJsonObject(),
tempEle2.getAsJsonObject());
entry.setValue(mergedObj);
}
}
}
Set<Entry<String, JsonElement>> entrySet2 = json2Obj.entrySet();
for (Entry<String, JsonElement> entry : entrySet2) {
String key2 = entry.getKey();
if (json1Obj.get(key2) == null) {
json1Obj.add(key2, entry.getValue());
}
}
return json1Obj;
}
Consider using a library that does this job for you, like JSON Merge, available at Maven Central.
You will get the desired result with a single line of code (you may ignore the String declarations if you already have the JSONObjects previously loaded):
String json1 = "{\n"
+ " \"glossary\": {\n"
+ " \"title\": \"example glossary\",\n"
+ " \"GlossDiv\": {\n"
+ " \"title\": \"S\"\n"
+ " }\n"
+ " }\n"
+ " }";
String json2 = "{\n"
+ " \"glossary\": {\n"
+ " \"title\": \"person name\",\n"
+ " \"age\": \"25\"\n"
+ " }\n"
+ " }";
JSONObject result = new JsonMerger<>(JSONObject.class).merge(json2, json1);
Note: the first JSON parameter passed to the merge method will always have more precedence/importance than the second one in case of key collisions.
This library works with Jackson, Gson, and other JSON providers as well.
So I'm quite late to the party but I wanted to share my solution if anybody stumbles across this.
You can deeply merge two json strings with com.fasterxml.jackson.core:jackson-databind ObjectMapper.readerForUpdating().
In this scenario you pass in two Json as String and merge them via readerForUpdating (untested code):
public String mergeJsonStrings(String json1, String json2) {
ObjectMapper mapper = new ObjectMapper();
ObjectReader reader = mapper.readerForUpdating(json1);
String result = reader.readValue(json2);
return result;
}
I used similar code to merge a property into an existing dataset. In this example the SomeProperties class contains a hashmap which holds the properties for a specific user. The passed in propertiesString is a single dot separated property e.g. some.random.property=value. The property will be transformed into a JsonNode with com.fasterxml.jackson.dataformat:jackson-dataformat-properties.
public SomeProperties mergeProperties(SomeProperties someProperties, String propertiesString) {
JavaPropsMapper javaPropsMapper = new JavaPropsMapper();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = javaPropsMapper.readTree(propertiesString);
ObjectReader objectReader = mapper.readerForUpdating(someProperties.getProperties());
HashMap<String, Object> mergedProperties = objectReader.readValue(jsonNode);
someProperties.setProperties(mergedProperties);
return someProperties;
}
In both cases everything passed into objectReader.readValue() will override existing keys.
Is it possible to use Jackson library to manually parse JSON?
I.e. I don't want to use ObjectMapper and convert JSON to some object, but rather I want select some individual properties from JSON, like in XPath:
For example this is my JSON:
{
"person": {
"name": "Eric",
"surname": "Ericsson",
"address" {
"city": "LA",
"street": "..."
}
}
}
And all what I want is just to get Name and the City, for this cases I don't want introduce 2 new Java classes (Person and Address) and use them with ObjectMapper, but I'm just want to read this values like in xPath:
Pseudocode:
String name = myJson.get("person").get("name")
String city = myJson.get("person").get("address").get("city")
You can use the Jackson tree model and JsonNode#at(...) method which takes the Json Pointer expression as a parameter.
Here is an example:
public class JacksonJsonPointer {
static final String JSON = "{"
+ " \"person\": {"
+ " \"name\": \"Eric\","
+ " \"surname\": \"Ericsson\","
+ " \"address\": {"
+ " \"city\": \"LA\","
+ " \"street\": \"...\""
+ " }"
+ " }"
+ "}";
public static void main(String[] args) throws IOException {
final ObjectMapper mapper = new ObjectMapper();
final JsonNode json = mapper.readTree(JSON);
System.out.println(json.at("/person/name"));
System.out.println(json.at("/person/address/city"));
}
}
Output:
"Eric"
"LA"
Yes Using Json parser you can parse your Json, Below is a sample example you can find more in jackson documentation
JsonParser jsonParser = new JsonFactory().createJsonParser(jsonStr);
while(jsonParser.nextToken() != JsonToken.END_OBJECT){
String name = jsonParser.getCurrentName();
if("name".equals(name)) {
jsonParser.nextToken();
System.out.println(jsonParser.getText());
}
if("surname".equals(name)) {
jsonParser.nextToken();
System.out.println(jsonParser.getText());
}
if("city".equals(name)) {
jsonParser.nextToken();
System.out.println(jsonParser.getText());
}
}