Better way to remove the json node at deeper level - java

The below is my sample json file which conatins the information about the cars, I wanted to remove the "city" from the below json data at a time.
Since the json data contains too many city nodes at very depath, I don't want to parse through each and every node to remove the "city" node. I want to remove the "city"
node in the easiest way without parsing the each node. I looked into string replace with empty but It is large data, It may fail in some cases.
Could any one tell me, what could be the better approach to remove the "city" node.
{
"carDetails":
[
{
"name":"John",
"city":"Berlin",
"cars":[
"audi",
"bmw",
"skoda": {
"model": "f3z2",
"manfactureDetails": {
"city": "vegas",
"time": "123967878734",
"color": "white",
"rawMaterial": {
"city": "london",
"quality": 1,
"importedDetails":{
"city" : "chile",
"date": "12/jan/2015",
...........
}
}
}
}
],
"job":"Teacher"
},
{
"name":"Mark",
"city":"Oslo",
"cars":[
"VW",
"Toyata" {
"manfactureDetails": {
"city" : "losangels",
.................
..................
}
}
],
"job":"Doctor"
}
]
}

Using jsonia, you can remove the field "on the fly":
public static void main(String[] args) {
try (InputStream stream = new FileInputStream("test.json")) {
try (OutputStream os = new FileOutputStream("output.json");
Writer writer = new OutputStreamWriter(os, "UTF-8");
PrintWriter out = new PrintWriter(writer)) {
JSonHandler formatter = new JSonFormatter(out, true);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
JSonHandler handler = (JSonHandler) Proxy.newProxyInstance(cl,
new Class<?>[] {JSonHandler.class},
new MyHandler(formatter));
JSonParser.parse(stream, "UTF-8", handler);
}
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
The MyHandler class looks like this:
private static class MyHandler implements InvocationHandler {
private int level = 0;
private final Object delegate;
public MyHandler(Object delegate) {
this.delegate = delegate;
}
#Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (method.getName().equals("startField")
&& args[0].equals("city")) {
++level;
} else if (method.getName().equals("endField")
&& args[0].equals("city")) {
--level;
} else if (level == 0) {
return method.invoke(delegate, args);
}
return null;
}
}

Related

I want to add a new Object inside a Json Object in JAVA with JSONObject

Im trying to make a JAVA application that makes a json file with the data that i send, but when i send new data, the last data the data is just replaced
the first method called
az.addUser("John", "10", "star");
the JSON
{
"user" : {
"name": "john",
"score": "10",
"type": "star"
}
}
second method called
az.addUser("Kevin", "20", "energy");
The JSON Expected
{
"user" : {
"name": "john",
"score": "10",
"type": "star"
}
"user" : {
"name" = "Kevin",
"score" = "20",
"type" = "energy"
}
}
the REAL JSON
{
"user" : {
"name" = "Kevin",
"score" = "20",
"type" = "Energy"
}
}
The Method
public void addUser(String name, String score, String type){
FileWriter wf = new FileWriter("exit.json");
JSONObject json;
JSONObject jsonInternal = new JSONObject();
jsonInternal.put("name", name);
jsonInternal.put("score", score);
jsonInternal.put("type", type);
json = new JSONObject();
json.put("user", jsonInternal);
wf.write(json.toJSONString());
wf.close();
}
You need to write a JSON array, not a JSON object. The code below is strictly pseudocode, as I do not know which library JSONObject comes from.
import java.io.FileWriter;
import java.io.IOException;
public class UserListWriter {
private String filename;
private JSONArray usersJson;
public UserListWriter(String filename) {
this.filename = filename;
this.usersJson = new JSONArray();
}
public UserListWriter addUser(String name, int score, String type) {
JSONObject userJson = new JSONObject();
userJson.put("name", name);
userJson.put("score", score);
userJson.put("type", type);
usersJson.put(userJson);
return this;
}
public UserListWriter write() throws IOException {
FileWriter wf = new FileWriter(this.filename);
wf.write(usersJson.toJSONString());
wf.close();
return this;
}
public static void main(String[] args) {
try {
new UserListWriter("exit.json")
.addUser("John", 10, "star")
.addUser("Kevin", 20, "energy")
.write();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Theoretical output:
[{
"name": "John",
"score": 10,
"type": "star"
}, {
"name": "Kevin",
"score": 20,
"type": "energy"
}]

JSON Structure without Keys

I downloaded some information in json format, but it looks different from what I am regularly used to.
The basic structures consists of two objects: an array of arrays without keys and an array of objects with key:value pairs, indicating the "keys" for the first array and their type.
{
"datatable": {
"data": [
[
"2022-04-26",
118313,
0,
"QQQ",
null,
"BL6CD96",
"ARCAVA4600V8",
"XBUE",
"INVESCO QQQ TRUST SE1-CEDEAR",
"Invesco QQQ Trust Series 1",
"False",
"False"
],
[
"2022-04-26",
56360,
22669,
"QQQ",
"46090E103",
"BDQYP67",
"US46090E1038",
"XNAS",
"INVESCO QQQ TRUST SERIES 1",
"Invesco QQQ Trust Series 1",
"True",
"False"
],
[
"2022-04-26",
44307,
25533,
"IBM",
"459200101",
"2005973",
"US4592001014",
"XNYS",
"INTL BUSINESS MACHINES CORP",
"International Business Machines Corp",
"True",
"True"
]
],
"columns": [{
"name": "marketdate",
"type": "Date"
},
{
"name": "seckey",
"type": "Integer"
},
{
"name": "securityid",
"type": "Integer"
},
{
"name": "ticker",
"type": "text"
},
{
"name": "cusip",
"type": "text"
},
{
"name": "sedol",
"type": "text"
},
{
"name": "isin",
"type": "text"
},
{
"name": "mic",
"type": "text"
},
{
"name": "securityname",
"type": "text"
},
{
"name": "companyname",
"type": "text"
},
{
"name": "uslisted",
"type": "text"
},
{
"name": "innqgi",
"type": "text"
}
]
},
"meta": {
"next_cursor_id": null
}
}
Result I am trying to achieve:
{
"datatable": {
"data": [
[
"marketdate":"2022-04-26",
"seckey":118313,
"securityid":0,
"ticker":"QQQ",
"cusip":"null",
"sedol":"BL6CD96",
"isin":"ARCAVA4600V8",
"mic":"XBUE",
"securityname":"INVESCO QQQ TRUST SE1-CEDEAR",
"companyname":"Invesco QQQ Trust Series 1",
"uslisted":"False",
"innqgi":"False"
],
...
},
"meta": {
"next_cursor_id": null
}
}
How can I convert this into a regular key=value JSON OR
How do I parse this in Java so that I have a POJO where the variable names = "colName"?
Thanks a lot!
Nikhil
You need to map column names from second array to values from first array using indexes. First let's create POJO structure.
public class DataObject {
private LocalDate marketDate;
private int secKey;
private int securityId;
private String ticker;
private String cusip;
private String sedol;
private String isin;
private String mic;
private String securityName;
private String companyName;
private String uslisted;
private String innqgi;
//getters and setters
}
Then:
public class DataWrapper {
private List<DataObject> data;
//getters setters
}
Response:
public class Response {
private DataWrapper datatable;
//getters setters
//omitting meta
}
Then create deserializer to map column names to corresponding values:
public class ResponseDeserializer extends StdDeserializer<Response> {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private final Map<String, BiConsumer<JsonNode, DataObject>> map = new HashMap<>();
public ResponseDeserializer() {
super(Response.class);
this.initMap();
}
private void initMap() {
map.put("marketdate", (jsonNode, dataObject) -> dataObject.setMarketDate(LocalDate.parse(jsonNode.asText(), FORMATTER)));
map.put("seckey", (jsonNode, dataObject) -> dataObject.setSecKey(jsonNode.asInt()));
map.put("cusip", (jsonNode, dataObject) -> dataObject.setCusip(jsonNode.asText()));
//do the same for rest
}
#Override
public Response deserialize(JsonParser parser, DeserializationContext context) throws IOException {
JsonNode root = parser.getCodec().readTree(parser);
ArrayNode dataArray = (ArrayNode) root.get("datatable").get("data");
ArrayNode columnsArray = (ArrayNode) root.get("datatable").get("columns");
List<DataObject> objects = new ArrayList<>(dataArray.size());
for (int index = 0; index < dataArray.size(); index++) {
ArrayNode data = (ArrayNode) dataArray.get(index);
DataObject dataObject = new DataObject();
for (int dadaIndex = 0; dadaIndex < data.size(); dadaIndex++) {
JsonNode node = data.get(dadaIndex);
String columnName = columnsArray.get(dadaIndex).get("name").asText();
this.map.getOrDefault(columnName, (jsonNode, dataObject1) -> {}).accept(node, dataObject);
}
objects.add(dataObject);
}
DataWrapper wrapper = new DataWrapper();
wrapper.setData(objects);
Response response = new Response();
response.setDatatable(wrapper);
return response;
}
}
Here i am using a Map to map column name to operation setting the value, but you could do it with reflection as well, for example.
A serializer to parse local date to the same format as in input:
public class LocalDateSerializer extends StdSerializer<LocalDate> {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public LocalDateSerializer() {
super(LocalDate.class);
}
#Override
public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(FORMATTER.format(value));
}
}
Register serializers/deserializers and test result:
public class Main {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Response.class, new ResponseDeserializer());
module.addSerializer(LocalDate.class, new LocalDateSerializer());
mapper.registerModule(module);
Response response = mapper.readValue(inputJson, Response.class);
String json = mapper.writeValueAsString(response);
System.out.println(json);
}
}
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
class HashMapExample {
private static HashMap<String, Integer> bigHashMap;
public static void main(String[] args) {
try {
JSONObject jsonObject = getJsonFromSource();
// Get datatable object from JSONObject
JSONObject dataTable = (JSONObject) jsonObject.get("datatable");
if (dataTable != null) {
// Get JSONArray from JSONObject datatable
JSONArray data = dataTable.getJSONArray("data");
JSONArray columns = dataTable.getJSONArray("columns");
mapToKeyValuePair(data, columns); // Map key to value
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
private static void mapToKeyValuePair(final JSONArray dataArray, final JSONArray columnsArray) {
// Check for equal lengths
if ((dataArray != null) && (columnsArray != null)) {
ArrayList<String> fieldNames = new ArrayList<>(); // ArrayList with field names
ArrayList<String> dataValuesArrays = new ArrayList<>(); // ArrayList with the data values
ArrayList<HashMap> wholeFinalArray = new ArrayList<>(); // The whole array with key pair value
// Loop to get a JSONObject with all the column names
for (int i = 0; i < columnsArray.length(); i++) {
JSONObject jsonObjectColumn = (JSONObject) columnsArray.get(i); // Get JSONObject with column names
fieldNames.add(jsonObjectColumn.get("name").toString()); // Get fieldNames from JSONObject above
}
for (int i = 0; i < dataArray.length(); i++) {
JSONArray jsonArrayData = (JSONArray) dataArray.get(i); // Get JSONArray with data
dataValuesArrays.add(jsonArrayData.toString()); // Add the data to an ArrayList
}
// Loop through the data values combined arrays
for (String dataValuesArray : dataValuesArrays) {
JSONArray singleDataArray = new JSONArray(dataValuesArray); // Get single data array
for (int a = 0; a < singleDataArray.length(); a++) {
HashMap<String, String> item = new HashMap<>();
item.put(fieldNames.get(a), singleDataArray.get(a).toString());
wholeFinalArray.add(item);
}
}
System.out.println(wholeFinalArray);
}
}
private static JSONObject getJsonFromSource() {
String jsonResponse = "{'datatable':{'data': [['2022-04-26', 118313, 0, 'QQQ', null, " +
"'BL6CD96', " +
"'ARCAVA4600V8', 'XBUE', 'INVESCO QQQ TRUST SE1-CEDEAR', 'Invesco QQQ Trust Series 1', 'False', 'False'],['2022-04-26', 56360, 22669, 'QQQ', '46090E103', 'BDQYP67', 'US46090E1038', 'XNAS', 'INVESCO QQQ TRUST SERIES 1', 'Invesco QQQ Trust Series 1', 'True', 'False'],['2022-04-26', 44307, 25533, 'IBM', '459200101', '2005973', 'US4592001014', 'XNYS', 'INTL BUSINESS MACHINES CORP', 'International Business Machines Corp', 'True', 'True']],'columns': [{'name':'marketdate', 'type':'Date'},{'name':'seckey', 'type':'Integer'},{'name':'securityid', 'type':'Integer'},{'name':'ticker', 'type':'text'},{'name':'cusip', 'type':'text'},{'name':'sedol', 'type':'text'},{'name':'isin', 'type':'text'},{'name':'mic', 'type':'text'},{'name':'securityname', 'type':'text'},{'name':'companyname', 'type':'text'},{'name':'uslisted', 'type':'text'},{'name':'innqgi', 'type':'text'}]}, 'meta':{'next_cursor_id':null}}";
return new JSONObject(jsonResponse);
}
}

convert an json array to a POJO?(code wrkng on single array) [duplicate]

This question already has an answer here:
Json Array to Pojo
(1 answer)
Closed 4 years ago.
Following is the array of the json object
[
{
"name": " hh",
"place": "usa",
"isPres": false,
"id": {
"lId": {
"id1": "40",
"level1": "tte"
},
"space": "ua"
},
"isempty": null,
"isspace": true
},
{
"name": " GE",
"place": "guinea",
"isPres": true,
"id": {
"lId": {
"id1": "30",
"level1": "Le"
},
"space": "ma"
},
"isempty": null,
"isspace": false
}
]
I tried the below code to convert the JSON array to POJO
public class JsonToPojo {
public static void main(String[] args) {
String packageName="com.vogella.maven.quickstart";
File inputJson= new File("C:/projects/quickstart/input.json");
File outputPojoDirectory=new File("."+File.separator+"convertedPojo");
outputPojoDirectory.mkdirs();
try {
new JsonToPojo().convert2JSON(inputJson.toURI().toURL(), outputPojoDirectory, packageName, inputJson.getName().replace(".json", ""));
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("Encountered issue while converting to pojo: "+e.getMessage());
e.printStackTrace();
}
}
public void convert2JSON(URL inputJson, File outputPojoDirectory, String packageName, String className) throws IOException{
JCodeModel codeModel = new JCodeModel();
URL source = inputJson;
GenerationConfig config = new DefaultGenerationConfig() {
#Override
public boolean isGenerateBuilders() { // set config option by overriding method
return true;
}
public SourceType getSourceType(){
return SourceType.JSON;
}
};
SchemaMapper mapper = new SchemaMapper(new RuleFactory(config, new Jackson2Annotator(config), new SchemaStore()), new SchemaGenerator());
mapper.generate(codeModel, className, packageName, source);
codeModel.build(outputPojoDirectory);
}
}
but the problem is i am getting the java pojo class only for one object of the array and not for the two objects
i want the result for both the arrays
Just a suggestion :: If you are using eclipse you can use https://marketplace.eclipse.org/content/jsonizer
plugin to create your pojo automatically from Sample JSON
and then in your java code, you can use Jackson mapper
ObjectMapper mapper = new ObjectMapper();
String jsonInString = "{'name' : 'mkyong'}";
//JSON from String to Object
User user = mapper.readValue(jsonInString, User.class);

Parsing json which has an object in two formats

I have a json object as below.
{
"products": [
{
"details": {
"name": "xxx",
"price": "100rs"
},
"description": "Buy this product"
}, {
"details": [{
"name": "yyy",
"price": "200rs"
}],
"description": "another project"
}
]
}
Here the details are presented in 2 formats. How can I create a POJO (Plain Old Java Object) class of this to use for the Retrofit api?
I think that's bad api response and should be fixed from backend. But if you want to address the problem, you have to deserialize response to String using String converter. You can't do deserialize it to your Pojo using Gson converter.
StringConverter.java
public class StringConverter implements Converter {
#Override
public Object fromBody(TypedInput typedInput, Type type) throws ConversionException {
String text = null;
try {
text = fromStream(typedInput.in());
} catch (IOException ignored) { }
return text;
}
#Override
public TypedOutput toBody(Object o) {
return null;
}
public static String fromStream(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder out = new StringBuilder();
String newLine = System.getProperty("line.separator");
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append(newLine);
}
return out.toString();
}
}
API Call implementation
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(API_URL)
.setConverter(new StringConverter())
.build();
YourAPI api = restAdapter.create(YourAPI.class);
api.yourService(parameter,new RestCallback<String>() {
#Override
public void success(String response, Response retrofitResponse) {
super.success(response, retrofitResponse);
//process your response here
//convert it from string to your POJO, JSON Object, or JSONArray manually
}
#Override
public void failure(RetrofitError error) {
super.failure(error);
}
});

Parse a nested JSON using gson

{
"Response": {
"MetaInfo": {
"Timestamp": "2011-11-21T14:55:06.556Z"
},
"View": [
{
"_type": "SearchResultsViewType",
"ViewId": 0,
"Result": [
{
"Relevance": 0.56,
"MatchQuality": {
"Country": 1,
"State": 1,
"County": 1,
"City": 1,
"PostalCode": 1
},
"Location": {
"LocationType": "point",
"DisplayPosition": {
"Latitude": 50.1105,
"Longitude": 8.684
},
"MapView": {
"_type": "GeoBoundingBoxType",
"TopLeft": {
"Latitude": 50.1194932,
"Longitude": 8.6699768
},
"BottomRight": {
"Latitude": 50.1015068,
"Longitude": 8.6980232
}
},
"Address": {
"Country": "DEU",
"State": "Hessen",
"County": "Frankfurt am Main",
"City": "Frankfurt am Main",
"District": "Frankfurt am Main",
"PostalCode": "60311",
"AdditionalData": [
{
"value": "Germany",
"key": "CountryName"
}
]
}
}
}
]
}
]
}
}
I am trying to retrieve the postal code from the above JSON. I am using gson to parse it. I am very new to JSON and from what i read from all the posts here(some very similar to this), I understood that the fields name should be as it is. So I understand i have to make 4 classes viz Response, view, Result and Address. I made them static nested classes, but I am only getting null value as output. In the next JSON, I have multiple addresses. But I am stuck on this single response.
For a short example, I try to retrieve Timestamp with this code, but it gives me a null value
public class ParseJSON {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new FileReader("try.json"));
Gson gson = new GsonBuilder().create();
Pojo pojo = gson.fromJson(br,Pojo.class);
System.out.println(Pojo.Response.MetaInfo.Timestamp);
br.close();
}
}
class Pojo {
public Pojo() { }
static class Response{
static class MetaInfo {
static public String Timestamp;
public String getTimestamp() {
return Timestamp;
}
}
}
}
If you only need the "PostalCode", you could use JsonParser instead of having a bunch of classes:
JsonParser jsonParser = new JsonParser();
JsonObject address = jsonParser.parse(json)
.getAsJsonObject().get("Response")
.getAsJsonObject().getAsJsonArray("View").get(0)
.getAsJsonObject().getAsJsonArray("Result").get(0)
.getAsJsonObject().get("Location")
.getAsJsonObject().getAsJsonObject("Address");
String postalCode = address.get("PostalCode").getAsString();
or for all results:
JsonArray results = jsonParser.parse(json)
.getAsJsonObject().get("Response")
.getAsJsonObject().getAsJsonArray("View").get(0)
.getAsJsonObject().getAsJsonArray("Result");
for (JsonElement result : results) {
JsonObject address = result.getAsJsonObject().get("Location").getAsJsonObject().getAsJsonObject("Address");
String postalCode = address.get("PostalCode").getAsString();
System.out.println(postalCode);
}
To make your Timestamp example work, try:
public class ParseJSON {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new FileReader("try.json"));
Gson gson = new GsonBuilder().create();
Pojo pojo = gson.fromJson(br, Pojo.class);
System.out.println(pojo.Response.MetaInfo.Timestamp);
br.close();
}
}
class Pojo {
Response Response = new Response();
}
class Response {
MetaInfo MetaInfo = new MetaInfo();
}
class MetaInfo {
String Timestamp;
}

Categories