Splitting a Json to multiple Json String in Java efficiently - java

I have a json String like below and I want to split/explode it in to multiple json string.
input:
{
"name": "paddy",
"age": 29,
"cities": [
{
"cityName": "Chennai",
"year": "2013-2015"
},
{
"cityName": "Bangalore",
"year": "2015-2019"
}
]
}
And I want to convert in to two Json string
json 1
{
"name": "paddy",
"age": 29,
"cities": [
{
"cityName": "Chennai",
"year": "2013-2015"
}
]
}
json 2
{
"name": "paddy",
"age": 29,
"cities": [
{
"cityName": "Bangalore",
"year": "2015-2019"
}
]
}
As of now, my approach below using jackson library.
package com.test;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class JsonParser {
public static void main(String[] args) throws IOException {
String json =
"{\n"
+ "\t\"name\": \"paddy\",\n"
+ "\t\"age\": 29,\n"
+ "\t\"cities\": [\n"
+ "\t\t{\n"
+ "\t\t\t\"cityName\": \"Chennai\",\n"
+ "\t\t\t\"year\": \"2013-2015\"\n"
+ "\t\t},\n"
+ "\t\t{\n"
+ "\t\t\t\"cityName\": \"Bangalore\",\n"
+ "\t\t\t\"year\": \"2015-2019\"\n"
+ "\t\t}\n"
+ "\t]\n"
+ "}";
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
// Create a list to store the result (the list will store Jackson tree model objects)
List<JsonNode> result = new ArrayList<>();
JsonNode tree = mapper.readTree(json);
JsonNode paths = tree.get("cities");
Iterator<JsonNode> elements = paths.elements();
while (elements.hasNext()) {
JsonNode path = elements.next();
// Create a copy of the tree
JsonNode copyOfTree = mapper.valueToTree(tree);
((ArrayNode)copyOfTree.get("cities")).removeAll().add(path);
// Add the modified tree to the result list
result.add(copyOfTree);
}
// Print the result
for (JsonNode node : result) {
System.out.println(mapper.writeValueAsString(node));
System.out.println();
}
}
}
This above approach can work fine if the json is smaller. Is there any better approach to handle large json files. For example, assume the "cities" have million objects.
Thanks.

There is many different factors you need to consider. First, do not copy the whole root object. In case, you have a big cities array you just waste a memory for creating new copy and remove all elements from it. See below example:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.File;
import java.io.IOException;
public class JsonApp {
public static void main(String[] args) throws IOException {
File jsonFile = new File("./spring-basics/src/main/resources/test.json");
ObjectMapper mapper = new ObjectMapper();
// read whole JSON
ObjectNode root = (ObjectNode) mapper.readTree(jsonFile);
String citiesFieldName = "cities";
// remove cities from root, now it contains only common properties
ArrayNode cities = (ArrayNode) root.remove(citiesFieldName);
cities.elements().forEachRemaining(item -> {
// copy root
ObjectNode copyOfRoot = root.deepCopy();
// add one city to copy
copyOfRoot.set(citiesFieldName, copyOfRoot.arrayNode().add(item));
// add to result or send further
System.out.println(copyOfRoot);
});
}
}
Above code copies root and adds one element to cities array. Now, we need to think what to do with result. You can send it immediately for next processing or store in list and send it in bulk operation. Another improvement could be splitting cities arrays on bigger chunks, more than 1 element. See this article, how to split list. For example, in case you have 1_000_000 elements, split it on list of 1_000 elements chunks.

Related

Read multiple json array with different names dynamically

I have a JSON which has many arrays with a different name, just like below JSON.
{
"CA": [
{
"high": 5,
"low": 3,
"key": "ABPS"
},
{
"high": 6,
"low": 2,
"key": "ABPO"
}
],
"EE": [
{
"high": 8,
"low": 4,
"key": "ABPS"
},
{
"high": 7,
"low": 2,
"key": "ABPO"
}
]
}
I am trying to iterate JSON array values dynamically without specifying the name of the array.
I am able to read array with specifying the name of the array with below code but how to read array values dynamically without specifying the name of every array because the JSON file I have it has thousands of array.
package com.abc;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
public class JsonRead {
private static final String filePath = "jsonTestFile.json";
public static void main(String[] args) {
try (FileReader reader = new FileReader(ClassLoader.getSystemResource(filePath).getFile())) {
JSONParser jsonParser = new JSONParser();
JSONObject jsonObject = (JSONObject) jsonParser.parse(reader);
// get an array from the JSON object
JSONArray lang = (JSONArray) jsonObject.get("CA");
Iterator i = lang.iterator();
// take each value from the json array separately
while (i.hasNext()) {
JSONObject innerObj = (JSONObject) i.next();
System.out.println("high " + innerObj.get("high") + " low " + innerObj.get("low")+ " key " + innerObj.get("key"));
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
This might help
for (Iterator iterator = jsonObject.keySet().iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
JSONArray jArray = (JSONArray) jsonObject.get(key);
}

Print a jsonObject in a certain order

Is it possible to print a jsonObject in a certain order?
This is what I have
import NPC.NPCHandler;
import NPC.NPCDrops;
import com.google.gson.*;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.io.FileWriter;
/**
* #author Ruud.
*/
public class Main {
public static NPCDrops npcDrops = new NPCDrops();
public static NPCHandler npcHandler = new NPCHandler();
public static void main(String args[]) {
/*for (int i = 1; i < npcs.length; i++)
System.out.println(npcs[i].absX);*/
JSONObject NPCDefinition = new JSONObject();
NPCDefinition.put("New", "Tester");
NPCDefinition.put("B", "Test");
NPCDefinition.put("A", "Test");
NPCDefinition.put("Test", "Tester");
JSONArray NPCDefinitions = new JSONArray();
NPCDefinitions.add(0, NPCDefinition);
//NPCDefinitions.add(1, NPCDefinition);
try (FileWriter file = new FileWriter("./Json/NPCDefinitions.json")) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
JsonParser jp = new JsonParser();
JsonElement je = jp.parse(NPCDefinitions.toJSONString());
String prettyJsonString = gson.toJson(je);
System.out.println(prettyJsonString);
//file.write(prettyJsonString);
} catch (Exception e) {
e.printStackTrace();
}
}
}
This is what it returns
[
{
"New": "Tester",
"A": "Test",
"B": "Test",
"Test": "Tester"
}
]
Is it possible to return it like the way it was put into the object?
I want this, because I am converting an NPC system in a game to a new better system using JSON, but I want the order to be in a human-logical way, like
[
{
"id": 0,
"name": "Hans",
"examine": "Servant of the Duke of Lumbridge.",
"combat": 0,
"size": 1
},
{
"id": 1,
"name": "Man",
"examine": "One of many citizens.",
"combat": 2,
"drops": [
{
"id": 995,
"amount": "1000",
"chance": 0.50
},
{
"id": 4151,
"amount": "1",
"chance": 1
}
],
"size": 1
}
]
I didn't test it yet with these variables, but I am sure it will mess up the order too, so I want to know if there is a way to keep this order. I know this isn't possible with JSONObject, because it is an unordered list or something, but this can't be achieved in any other way?
JSON objects are unordered K/V sets, but it does not mean that strict order is required, therefore it can be fully implementation driven.
What you're missing here is mixing two different libraries: JSON Simple and Gson. I have never worked with JSON Simple before, but even a quick look to that library would reveal that its org.json.simple.JSONObject extends raw HashMap which order may differ in different JRE versions. Unlike org.json.simple.JSONObject, Gson's native com.google.gson.JsonObject preserves the insertion order (see more at JsonObject implementation -- just because it's implemented this way). The same mixing story goes to org.json.simple.JSONArray and com.google.gson.JsonArray. So, the easiest solution is just getting rid of JSON Simple in favor of not mixing two different libraries, and just using Gson facilities, like this:
private static final Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
public static void main(final String... args) {
final JsonObject npcDefinition = new JsonObject();
npcDefinition.addProperty("New", "Tester");
npcDefinition.addProperty("B", "Test");
npcDefinition.addProperty("A", "Test");
npcDefinition.addProperty("Test", "Tester");
final JsonArray npcDefinitions = new JsonArray();
npcDefinitions.add(npcDefinition);
final String json = gson.toJson(npcDefinitions);
System.out.println(json);
}
Also note, that no toString() invocation is necessary (it may be really expensive due to in-memory string allocations) here because Gson works with its JsonElement sub-classes just perfect. Thus, the original order is preserved:
[
{
"New": "Tester",
"B": "Test",
"A": "Test",
"Test": "Tester"
}
]
Since the objects are meant to be unordered, you should not really rely even on this implementation (I'm using Gson 2.8.0) -- what if someday a newer Gson implementation would break this rule and break the backwards compatibility in a major release (let's say, hypothetic Gson 3) for whatever reason? This seems to be relevant to serializers/deserializers that use reflection to produce/consume JSON (for example, ReflectiveTypeAdapterFactory). In order to guarantee the order, the most robust solution I could think of is direct write to output streams, say something like this:
#SuppressWarnings("resource")
public static void main(final String... args)
throws IOException {
final JsonWriter jsonWriter = new JsonWriter(new OutputStreamWriter(System.out));
jsonWriter.setIndent(" ");
jsonWriter.beginArray();
jsonWriter.beginObject();
jsonWriter.name("New").value("Tester");
jsonWriter.name("B").value("Tester");
jsonWriter.name("A").value("Tester");
jsonWriter.name("Test").value("Tester");
jsonWriter.endObject();
jsonWriter.endArray();
jsonWriter.flush();
}
[
{
"New": "Tester",
"B": "Tester",
"A": "Tester",
"Test": "Tester"
}
]
JSON-Objects don't care about order of their attributes.
It may be that there are implementations of JSON-libraries that handle the input order. But I do not suggest to rely on the input order handled by any JSON-library as an JSON-Object is and should remain the assertion to be associative (key-value) and the keys are unordered.
So there is no intrinsic possibility to preserve the input order. The only way is to simulate it: You have to remember an order id and write your own ouputter that considers the order id during output.
The way you define your model will impact the order of your serialisation to Json. Something like this should work :
public class MyModel {
public int id;
public string name;
public string examine;
public int combat;
public List<Drop> drops;
public int size;
}

Convert newick hierarchical string to hierarchical JSON object using Java

I am currently studying newick format. https://en.wikipedia.org/wiki/Newick_format.
I have a newick string of a tree
(ABC,(STU,VWX)DEF,(GHI,JKL)MNO)PQR;
How to convert this string into a hierarchical JSON object like
JSONObject tree = {
name: 'PQR',
children: [{
name: 'ABC'
}, {
name: 'DEF',
children: [{
name: 'STU'
}, {
name: 'VWX'
}]
}, {
name: 'MNO',
children: [{
name: 'GHI'
}, {
name: 'JKL'
}]
}]
}
This is what i have tried but could not think further of how to fill the children of root node
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class Series1 {
public static void main(String[] args) throws JSONException
{
String data="(ABC,(STU,VWX,EFG)DEF,YZA,HIJ,(GHI,JKL)MNO,BCD)PQR";
JSONObject tree=new JSONObject();
tree.put("name",data.substring(data.lastIndexOf(")")+1,data.length()));
tree.put("children", getChildren(data.substring(1,data.lastIndexOf(")"))));
}
public static JSONArray getChildren(String children) throws JSONException
{
JSONArray childrenArray=new JSONArray();
List<Integer> commaIndexList=new ArrayList<Integer>();
List<String> childrenStringList=new ArrayList<String>();
for (int index = children.indexOf(",");index >= 0;index = children.indexOf(",", index + 1))
{
if(children.substring(index+1, index+2).equalsIgnoreCase("("))
{
commaIndexList.add(index);
System.out.println(index);
}
}
childrenStringList.add(children.substring(0, commaIndexList.get(0)));
childrenStringList.add(children.substring(commaIndexList.get(commaIndexList.size()-1)+1));
for(int i=0;i<commaIndexList.size()-1;i++)
{
childrenStringList.add(children.substring(commaIndexList.get(i)+1, commaIndexList.get(i+1)));
}
for(String childrenString:childrenStringList)
{
JSONObject childObject=new JSONObject();
if(childrenString.lastIndexOf(")")>0)
{
childObject.put("name", childrenString.substring(childrenString.lastIndexOf(")")+1));
childObject.put("children", getChildren(childrenString.substring(childrenString.indexOf("(")+1,childrenString.lastIndexOf(")"))));
}
else
{
childObject.put("name",childrenString);
}
childrenArray.put(childObject);
}
return childrenArray;
}
}
I'd say this problem is similar to evaluating math expressions, e.g. 2+5*(10-3)=?
+
2 *
5 -
10 3
The key is to use stack operations to remake the 'inorder' tree structure into 'postorder' which is in this case 2 5 10 3 - * +
This is a definite form without parentheses and so easily readable for machine processing. If you are interested, i can have a look at it.

json mapper writerWithDefaultPrettyPrinter for multiple json objects separated by new line

I had multi line json Strings separated by new line character. I am using jackson version 1.9.13 For example
{"name":"firstPerson","age":25,"gender":"male"}\n
{"name":"secondPerson","age":30,"gender":"male"}\n
{"name":"thirdPerson","age":25,"gender":"male"}\n
...............
The delimiter may be \n\r or \n based on the operating system using. Basically it is a batch record processing where we save all the contents back to database . But we also save the entire request as well .
My requirement is to get the multiple lines of json object when i use in prettyprint format.Please find the code
import java.io.IOException;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
public class Main {
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, JsonParseException, IOException {
//mapper.configure(Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
//mapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
String json = "{\"name\":\"firstPerson\",\"age\":25,\"gender\":\"male\"}\n{\"name\":\"secondPerson\",\"age\":30,\"gender\":\"male\"}\n{\"name\":\"thirdPerson\",\"age\":25,\"gender\":\"male\"}";
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectMapper.readValue(json, Object.class)));
System.out.println(json);
}
}
The out put is :
{
"name" : "firstPerson",
"age" : 25,
"gender" : "male"
}
But i want to display as :
{
"name" : "secondPerson",
"age" : 30,
"gender" : "male"
}
{
"name" : "thirdPerson",
"age" : 25,
"gender" : "male"
}
{
"name" : "firstPerson",
"age" : 25,
"gender" : "male"
}
I know its not a standard way of json string. json string always enclosed in flower brasis . But as it is already existing system i dont want to break the system to change the code . It can be done simply using any of the string handling functions in java but i need to use jackson and jackson's pretty printer.
Is there a way to get this done ?
Thanks,
Eresh
Your json string is made of 3 JSON objects, so you need to format them one at the time and then concatenate them:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
String json = "{\"name\":\"firstPerson\",\"age\":25,\"gender\":\"male\"}\n{\"name\":\"secondPerson\",\"age\":30,\"gender\":\"male\"}\n{\"name\":\"thirdPerson\",\"age\":25,\"gender\":\"male\"}";
String[] jsons = json.split("\n");
String output = "";
for (String str : jsons) {
output += objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectMapper.readValue(str, Object.class)) + "\n";
}
System.out.println(output);
String person= "{\"name\":\"firstPerson\",\"age\":25,\"gender\":\"male\"}\n{\"name\":\"secondPerson\",\"age\":30,\"gender\":\"male\"}\n{\"name\":\"thirdPerson\",\"age\":25,\"gender\":\"male\"}";
Object json = mapper.readValue(person, Object.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json));
Just like:
On demand
ObjectMapper objectMapper = new ObjectMapper();
Object obj = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectMapper.readValue(stringValue, Object.class));
For older version replace writerWithDefaultPrettyPrinter() with defaultPrettyPrinterWriter()
Globally
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
Object obj = objectMapper.writeValueAsString(objectMapper.readValue(stringValue, Object.class));

fill array from json file without knowing the keys

I have the following Json file and I want to create list of array ,i don't know the keys name (like person ,lastname etc ) it can be
many entities like employes users etc but the structure of the file must be exactly the same ,how can i do that ?
the file is
{
"Person": [
{
"name": "Peter",
"lastname": "ta",
"age": 43,
"sex": "male"
},
{
"name": "Zara",
"lastname": "treg",
"age": 25,
"sex": "female"
}
]
}
what I need is to create list of array like this
person ,name,peter ,lastname,ta,age,43,sex,male
person ,name,zara ,lastname,treg,age,23,sex,female
....
I started with the following code to get the file but since i dont know the name of the keys I dont know how to proceed.
JSONObject jsonObject= (JSONObject) parser.parse(new FileReader("C:\\General\\jsonperson.txt"));
You can use
String[] keyNames= JSONObject.getNames(jsonObject);
to get the names of the keys.
Javadoc
Using this, you can get the values using getJSONObject
Looping these you can construct the array you are looking for.
Check Example 4 on this page: https://code.google.com/p/json-simple/wiki/DecodingExamples
Specifically, this part:
Map json = (Map)parser.parse(jsonText, containerFactory);
Iterator iter = json.entrySet().iterator();
System.out.println("==iterate result==");
while(iter.hasNext()){
Map.Entry entry = (Map.Entry)iter.next();
System.out.println(entry.getKey() + "=>" + entry.getValue());
}
System.out.println("==toJSONString()==");
System.out.println(JSONValue.toJSONString(json));
That's how you might iterate over the entries of a JSONObject. By the way, with this library, if it's the one I think you're using, a JSONObject is just a java.util.Map, and you can use all of its methods - that's why it works for this example to cast the parse result to a Map.
All of the JSON <-> Java object mappings for this lib: https://code.google.com/p/json-simple/wiki/MappingBetweenJSONAndJavaEntities
You can also try the below code:
public void performExecute() throws IOException {
JsonFactory jsonFactory = new JsonFactory();
ObjectMapper objectMapper = new ObjectMapper(jsonFactory);
File file = new File("json.txt");
TypeReference<HashMap<String, Object>> typeReference = new TypeReference<HashMap<String, Object>>() {};
HashMap<String, Object> jsonMap = objectMapper.readValue(file, typeReference);
System.out.println("JSON DATA: " + jsonMap);
}
Make sure the Jackson library is in your class path & you imports the following classes while using the code:
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;

Categories