I am new on JSON, I want to read the JSON document using device id, I have many document on single file, just like as a database:
{
"deviceType":"AccessPoint",
"transactionType":"response",
"messageType":"set_config",
"classes":[
{
"deviceType":"AccessPoint",
"classId":1,
"ipv4":"192.168.100.100",
"netmask":"192.168.100.100",
"ipv6":"192.168.100.100",
"className":"Interface",
"interfaceName":"wlan0",
"state":"UP",
"type":"wireless",
"deviceId":"1234",
"status":"waiting"
}
],
"deviceId":"1234",
"transactionId":"201675"
}
Sometimes, classes array contain multiple arrays like indexes [{},{},..].
So, how can I read the doc using search criteria with java web application.
Just out of curiosity , did something on Jackson (streaming parser , since there is mention of large number of elements). I am just doing an output as name : value , this can be logically enhanced to suit your tastes. This is not even close to production , but is a good start.
import java.io.FileInputStream;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
public class TestClass {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("yourpath/transactions.json")) {
JsonFactory jf = new JsonFactory();
JsonParser jp = jf.createParser(fis);
//System.out.println(JsonToken.START_ARRAY);
// jp.setCodec(new ObjectMapper());
jp.nextToken();
while (jp.hasCurrentToken()) {
if (jp.getCurrentToken().equals(JsonToken.START_OBJECT)
|| jp.getCurrentToken().equals(JsonToken.START_ARRAY)
|| jp.getCurrentToken().equals(JsonToken.END_ARRAY)
|| jp.getCurrentToken().equals(JsonToken.END_OBJECT)) {
} else {
System.out.print(jp.getText());
jp.nextToken();
System.out.print(":");
System.out.print(jp.getText());
System.out.println();
}
jp.nextToken();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Related
I need to use a JsonParser twice, once to validate the format of my JsonStream by the json schema given..., then I need to contruct my object 'Product'.
The problem is that if I use the parser once, I cannot re-use it a second time. It's like it loses its data values .
Here is my Original code where I construct my Product object using the parser.. :
import javax.json.bind.serializer.DeserializationContext;
import javax.json.bind.serializer.JsonbDeserializer;
import javax.json.stream.JsonParser;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class ProductDeserializer implements JsonbDeserializer<Product>
{
#Override
public Product deserialize(final JsonParser parser, final DeserializationContext ctx, final Type rtType)
{
Product product = new Product();
while (parser.hasNext())
{
JsonParser.Event event = parser.next();
if (event == JsonParser.Event.KEY_NAME && parser.getString().equals("productNumber"))
{
parser.next();
product.setProductNumber(parser.getString());
This works fine But I need to include this validation of the json format first..:
ObjectMapper objectMapper = new ObjectMapper();
JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909);
String schemaStream = "My json schema here..";
JsonNode json = null;
try
{
json = objectMapper.readTree(parser.getObject().toString());
}
catch (JsonProcessingException eParam)
{
eParam.printStackTrace();
}
JsonSchema schema = schemaFactory.getSchema(schemaStream);
Set<ValidationMessage> validationResult = schema.validate(json);
if (validationResult.isEmpty()) {
System.out.println("no validation errors ");
} else {
System.out.println("There are validation errors ");
}
So if I try to include this part then the contruction of my Product Object will not work anymore and the Product will be null..
So my question is how can I use the parser twice in the same method ..
Thanks a lot in advance..
I'm trying to remove few nodes from JSON file. But I'm getting class cast exception when I try to remove node.
java.lang.ClassCastException: class com.fasterxml.jackson.databind.node.TextNode cannot be cast to class com.fasterxml.jackson.databind.node.ObjectNode (com.fasterxml.jackson.databind.node.TextNode and com.fasterxml.jackson.databind.node.ObjectNode are in unnamed module of loader 'app')
I tried other stackoverflow useful links, but not working for me. Here is the code that I have written.
public static void removeNodes(String filePath, String... nodeNames) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNodes = objectMapper.readTree(new File(filePath));
for (JsonNode node : jsonNodes) {
((ObjectNode) node).remove(Arrays.asList(nodeNames));
}
objectMapper
.writerWithDefaultPrettyPrinter()
.writeValue(new File(filePath.split("\\.")[0] + "_modified.json"), jsonNodes);
}
This is the json file I'm reading.
{
"boolean_key": "--- true\n",
"empty_string_translation": "",
"key_with_description": "Check it out! This key has a description! (At least in some formats)",
"key_with_line-break": "This translations contains\na line-break.",
"nested": {
"deeply": {
"key": "Wow, this key is nested even deeper."
},
"key": "This key is nested inside a namespace."
},
"null_translation": null,
"pluralized_key": {
"one": "Only one pluralization found.",
"other": "Wow, you have %s pluralizations!",
"zero": "You have no pluralization."
},
"Dog_key": {
"nest": "Only one pluralization found.",
"sample_collection": [
"first item",
"second item",
"third item"
],
"Pest": "Only two pluralization found."
},
"simple_key": "Just a simple key with a simple message.",
"unverified_key": "This translation is not yet verified and waits for it. (In some formats we also export this status)"
}
Caller:
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
// import static org.junit.jupiter.api.Assertions.*;
#RunWith(SpringRunner.class)
#SpringBootTest
class JSONUtilTest {
#Test
void removeNodes() throws IOException {
JSONUtil.removeNodes("D:\\data\\JSON_A.json", "pluralized_key", "Dog_key", "simple_key");
}
}
Can you help me to figure out what is causing the problem?
As mentioned in the comment, you need to first parse the JSON then do operations on it.
Read file.
Parse and convert it to JSONObject using JSONParser's parse() method.
Remove the nodes you want.
Using ObjectMapper's writerWithDefaultPrettyPrinter() method you get proper indentation and write that to output file.
Here is the code:
public static void removeNodes(String filePath, String... nodeNames) {
try (FileReader fileReader = new FileReader(filePath)) {
JSONParser jsonParser = new JSONParser();
JSONObject jsonObject = (JSONObject) jsonParser.parse(fileReader);
Stream<String> nodeStream = Stream.of(nodeNames);
nodeStream.forEach(jsonObject::remove);
ObjectMapper objectMapper = new ObjectMapper();
String jsonObjectPrettified = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
FileWriter fileWriter = new FileWriter(filePath.split("\\.")[0] + "_modified.json");
fileWriter.write(jsonObjectPrettified);
fileWriter.close();
} catch (IOException | ParseException e) {
e.printStackTrace();
}
}
I have nested JSON with bunch of children objects, but I just need response_time and question, subquestions of survey_data. What is the best way to parse nested JSON in rest controller to the object in spring?
{
"is_test_data":false,
"language":"English",
"url_variables":{
"requestId":{
"key":"requestId",
"value":"1"
}
},
"response_time":1114,
"survey_data":{
"2":{
"id":2,
"type":"parent",
"question":"For each of the following factors, please rate your recent project",
"subquestions":{
"10":{
"10001":{
"id":10001,
"type":"MULTI_TEXTBOX",
"question":"Overall Quality : Rating",
"answer":null,
}
},
"11":{
"10001":{
"id":10001,
"type":"MULTI_TEXTBOX",
"question":"Achievement of Intended Objectives : Rating",
"answer":null
}
}
}
},
"33":{
"id":33,
"type":"HIDDEN",
"question":"Submitted",
"answer_id":0,
}
}
}
Thank you.
What you should do is parse the complete json to jsonObject using json-simple jar
which create a map like structure for the json and then you can simply get the desired value from it using the key as I explained in below example
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
public class JsonDeserializer {
public static void main(String[] args) throws Exception {
File file = new File("test.json");
InputStream is = new FileInputStream(file);
StringBuilder textBuilder = new StringBuilder();
try (Reader reader = new BufferedReader(
new InputStreamReader(is, Charset.forName(StandardCharsets.UTF_8.name())))) {
int c = 0;
while ((c = reader.read()) != -1) {
textBuilder.append((char) c);
}
}
String jsonTxt = textBuilder.toString();
Object obj = new JSONParser().parse(jsonTxt);
JSONObject jo = (JSONObject) obj;
System.out.println(jo.get("response_time"));
}
}
JSON is a data communication format that is lightweight, text-based. Objects and arrays are two structured kinds that JSON can represent. A JSONArray may extract text from a String and convert it to a vector-like object. The getString(index) method of JSONArray can be used to parse a nested JSON object. This is a helper method for the getJSONString method (index). The getString() method returns a string.
I'm storing ChatColor objects stored in a class object. After successfully converting them to json using the Gson api, I want to be able to instantiate ChatColor objects in memory from this stored json file.
Using the code below, I get ChatColor objects who's toString methods return §6. they should be returning the same but minus the leading  character
To make this question easier to read I've reduced the examples down to a single ChatColor object per Theme object.
Theme.java
package core.data.objects;
import java.util.Map;
import net.md_5.bungee.api.ChatColor;
public class Theme {
private ChatColor primary, secondary, tertiary, clear, faded, succeed, fail;
public Theme(Map<String, ChatColor> thisMap) {
this.primary = thisMap.get("primary");
}
public ChatColor getPrimary() { return primary; }
public void setPrimary(ChatColor primary) { this.primary = primary; }
}
ThemeManager.java
package core.data;
import core.data.objects.Theme;
import java.io.*;
import java.util.Map;
import java.util.HashMap;
import com.google.gson.Gson;
import net.md_5.bungee.api.ChatColor;
public class ThemeManager {
public static Theme currentTheme;
public static void load() throws IOException {
try {
currentTheme = getThemeFromJSON(FileManager.defaultThemeFile);
System.out.println(ChatColor.GOLD);
System.out.println(currentTheme.getPrimary());
} catch (Exception e) {
currentTheme = createDefaultTheme();
System.out.println("WARN getThemeFromJSON Exception");
throw new IOException(e.getMessage());
}
}
public static Theme createDefaultTheme() {
Map<String, ChatColor> thisMap = new HashMap<>();
thisMap.putIfAbsent("primary", ChatColor.GOLD);
return new Theme(thisMap);
}
public static void writeThemeToJSON(Theme thisTheme, File thisFile) throws IOException {
Gson gson = new Gson();
Writer writer = new FileWriter(thisFile, false);
gson.toJson(thisTheme, writer);
writer.flush(); writer.close();
}
public static Theme getThemeFromJSON(File thisFile) throws FileNotFoundException {
Gson gson = new Gson();
Reader reader = new FileReader(thisFile);
return gson.fromJson(reader, Theme.class);
}
}
Console output of ThemeManager.load()
[20:20:56 INFO]: §6
[20:20:56 INFO]: §6
Example of saved .json file
{
"primary": {
"toString": "§6",
"name": "gold",
"ordinal": 6,
"color": {
"value": -22016,
"falpha": 0.0
}
}
}
The  comes out of nowhere!
Solution credit: #dave_thompson_085
The project (and the .json file) are encoded in UTF-8, but I was not reading the file using UTF-8 encoding. Apparently that is not the default encoding of a FileReader object.
By changing this:
public static Theme getThemeFromJSON(File thisFile) throws FileNotFoundException {
Gson gson = new Gson();
Reader reader = new FileReader(thisFile);
return gson.fromJson(reader, Theme.class);
}
to this:
public static Theme getThemeFromJSON(File thisFile) throws FileNotFoundException {
Gson gson = new Gson();
Reader reader = new InputStreamReader (
new FileInputStream (thisFile), StandardCharsets.UTF_8);
return gson.fromJson(reader, Theme.class);
}
, the stdout lines from ThemeManager.load() are now both showing §6 as expected.
However, note that when using aThemeObject.getPrimary(), the returned ChatColor object was not able to be used in place of regular calls to ChatColor.COLOR.
Trying to do so would simply result in "null" appearing in the displayed text in-game.
To fix this, I modified the get methods to:
public ChatColor getPrimary() {
return ChatColor.getByChar(primary.toString().charAt(1));
}
char at index 1 is the color code used by the getByChar method to return a clone chatcolor object.
This will prevent using custom falpha values and custom color values in the theme.json file, but ensures the resulting ChatColor object from calling Theme.getPrimary() can always be used in place of a regular call to ChatColor.COLOR
I store all static data in the JSON file. This JSON file has up to 1000 rows. How to get the desired data without storing all rows as ArrayList?
My code, I'm using right now and I want to increase its efficiency.
List<Colors> colorsList = new ObjectMapper().readValue(resource.getFile(), new TypeReference<Colors>() {});
for(int i=0; i<colorsList.size(); i++){
if(colorsList.get(i).getColor.equals("Blue")){
return colorsList.get(i).getCode();
}
}
Is it possible? My goal is to increase efficiency without using ArrayList. Is there a way to make the code like this?
Colors colors = new ObjectMapper().readValue(..."Blue"...);
return colors.getCode();
Resource.json
[
...
{
"color":"Blue",
"code":["012","0324","15478","7412"]
},
{
"color":"Red",
"code":["145","001","1","7879","123984","89"]
},
{
"color":"White",
"code":["7","11","89","404"]
}
...
]
Colors.java
class Colors {
private String color;
private List<String> code;
public Colors() {
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public List<String> getCode() {
return code;
}
public void setCode(List<String> code) {
this.code = code;
}
#Override
public String toString() {
return "Colors{" +
"color='" + color + '\'' +
", code=" + code +
'}';
}
}
Creating POJO classes in this case is a wasting because we do not use the whole result List<Colors> but only one internal property. To avoid this we can use native JsonNode and ArrayNode data types. We can read JSON using readTree method, iterate over array, find given object and finally convert internal code array. It could look like below:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
ArrayNode rootArray = (ArrayNode) mapper.readTree(jsonFile);
int size = rootArray.size();
for (int i = 0; i < size; i++) {
JsonNode jsonNode = rootArray.get(i);
if (jsonNode.get("color").asText().equals("Blue")) {
Iterator<JsonNode> codesIterator = jsonNode.get("code").elements();
List<String> codes = new ArrayList<>();
codesIterator.forEachRemaining(n -> codes.add(n.asText()));
System.out.println(codes);
break;
}
}
}
}
Above code prints:
[012, 0324, 15478, 7412]
Downside of this solution is we load the whole JSON to memory which could be a problem for us. Let's try to use Streaming API to do that. It is a bit difficult to use and you must know how your JSON payload is constructed but it is the fastest way to get code array using Jackson. Below implementation is naive and does not handle all possibilities so you should not rely on it:
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
System.out.println(getBlueCodes(jsonFile));
}
private static List<String> getBlueCodes(File jsonFile) throws IOException {
try (JsonParser parser = new JsonFactory().createParser(jsonFile)) {
while (parser.nextToken() != JsonToken.END_OBJECT) {
String fieldName = parser.getCurrentName();
// Find color property
if ("color".equals(fieldName)) {
parser.nextToken();
// Find Blue color
if (parser.getText().equals("Blue")) {
// skip everything until start of the array
while (parser.nextToken() != JsonToken.START_ARRAY) ;
List<String> codes = new ArrayList<>();
while (parser.nextToken() != JsonToken.END_ARRAY) {
codes.add(parser.getText());
}
return codes;
} else {
// skip current object because it is not `Blue`
while (parser.nextToken() != JsonToken.END_OBJECT) ;
}
}
}
}
return Collections.emptyList();
}
}
Above code prints:
[012, 0324, 15478, 7412]
At the end I need to mention about JsonPath solution which also can be good if you can use other library:
import com.jayway.jsonpath.JsonPath;
import net.minidev.json.JSONArray;
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
public class JsonPathApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
JSONArray array = JsonPath.read(jsonFile, "$[?(#.color == 'Blue')].code");
JSONArray jsonCodes = (JSONArray)array.get(0);
List<String> codes = jsonCodes.stream()
.map(Object::toString).collect(Collectors.toList());
System.out.println(codes);
}
}
Above code prints:
[012, 0324, 15478, 7412]
You can use DSM stream parsing library for memory, CPU efficiency and fast development. DSM uses YAML based mapping file and reads the whole data only once.
Here is the solution of your question:
Mapping File:
params:
colorsToFilter: ['Blue','Red'] # parameteres can be passed programmatically
result:
type: array
path: /.*colors # path is regex
filter: params.colorsToFilter.contains(self.data.color) # select only color that exist in colorsToFilter list
fields:
color:
code:
type: array
Usage of DSM to parse json:
DSM dsm = new DSMBuilder(new File("path/maping.yaml")).create(Colors.class);
List<Colors> object = (List<Colors>) dsm.toObject(jsonData);
System.out.println(object);
Output:
[Colors{color='Blue', code=[012, 0324, 15478, 7412]}, Colors{color='Red', code=[145, 001, 1, 7879, 123984, 89]}]