I have converted a DOM document to json String. However, there are some issues with the way List is mapped in scenario where the List has only one value and List has multiple values.
For ex:
1) After DOM document has been convered to json string, here AlphaStatus List has only with one value:
{
"Gamma": {
.
.
.
.
"AlphaStatuses": {
"AlphaStatus": {
"AlphaHeaderKey": "201612221122273660",
"AlphaLineKey": "201612221122273661",
}
},
"Delta": {
...
}
}
}
2) After DOM document has been convered to json string, here AlphaStatus List has only with multiple values is shown as:
{
"Gamma": {
.
.
.
.
"AlphaStatuses": {
"AlphaStatus": [
{
"AlphaHeaderKey": "201612221122273660",
"AlphaLineKey": "201612221122273661",
},
{
"AlphaHeaderKey": "201612221122273660",
"AlphaLineKey": "201612221122273662",
},
{
"AlphaHeaderKey": "201612221122273660",
"AlphaLineKey": "2016}2221122273663",
}
]
},
"Delta": {
...
}
}
}
I am using the below jackson code to convert xml string to json:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
Object json = mapper.readValue(jObject.toString(), Object.class);
String output = mapper.writeValueAsString(json);
My question is, how do i ensure that AlphaStatus List is always starting with [{ and ending with }], no matter whether it has only one value or multiple values. How can this be resolved.
It is causing issues in the other system which assumes that AlphaStatus is a List always and expects [{ to be part of the token.
Any help is appreciated.? Or should i use some string utility in such cases to parse AlphaStatus and replace with [{ and }]. How can this be done
First, it seems the line
Object json = mapper.readValue(jObject.toString(), Object.class);
is useless, because you already have an object (jObject) to serialize.
Just use it:
String output = mapper.writeValueAsString(jObject);
For second, it seems your problematic field is of type java.lang.Object, right?
If you as assign a single value to it, it will result in one single Json object:
jObject.setAlphaStatuses(alphaStatus); -> result -> {...}
If you as assign some kind of collection, it will result in a Json array:
jObject.setAlphaStatuses(Arrays.asList(alphaStatus1, alphaStatus2)); -> result -> [{...},{...}]
To avoid that, either always pass a list or (if you can change the definition of the class) make it to a Collection (maybe some List).
Here a small snippet to test:
import java.util.Arrays;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonObjects {
private final static ObjectMapper mapper = new ObjectMapper();
private final static AlphaStatus as1 = new AlphaStatus();
private final static AlphaStatus as2 = new AlphaStatus();
static {
as1.setAlphaHeaderKey("A");
as1.setAlphaLineKey("B");
as2.setAlphaHeaderKey("C");
as2.setAlphaLineKey("D");
}
public static void main(String[] args) throws JsonProcessingException {
final Gamma gamma = new Gamma();
gamma.setAlphaStatuses(Arrays.asList(as1, as2));
System.out.println(mapper.writeValueAsString(gamma));
gamma.setAlphaStatuses(as1);
System.out.println(mapper.writeValueAsString(gamma));
}
static class Gamma {
Object alphaStatuses;
public Object getAlphaStatuses() {
return alphaStatuses;
}
public void setAlphaStatuses(Object alphaStatuses) {
this.alphaStatuses = alphaStatuses;
}
}
static class AlphaStatus {
String alphaHeaderKey;
String alphaLineKey;
public String getAlphaHeaderKey() {
return alphaHeaderKey;
}
public void setAlphaHeaderKey(String alphaHeaderKey) {
this.alphaHeaderKey = alphaHeaderKey;
}
public String getAlphaLineKey() {
return alphaLineKey;
}
public void setAlphaLineKey(String alphaLineKey) {
this.alphaLineKey = alphaLineKey;
}
}
}
And the result (not exactly your result, only for demonstration):
{"alphaStatuses":[{"alphaHeaderKey":"A","alphaLineKey":"B"},{"alphaHeaderKey":"C","alphaLineKey":"D"}]}
{"alphaStatuses":{"alphaHeaderKey":"A","alphaLineKey":"B"}}
#JsonRootName("Gamma")
public class Gamma {
private AlphaStatuses AlphaStatuses;
// getters and setters
}
public class AlphaStatuses {
#JsonProperty("alphaStatus")
private List<AlphaStatus> alphaStatuses;
// getters and setters
}
public class AlphaStatus{
#JsonProperty("alphaHeaderKey")
private String alphaHeaderKey;
#JsonProperty("alphaLineKey")
private String alphaLineKey;
// getters and setters
}
**Test class**:
#Test
public void test() throws Exception {
Gamma gamma=new Gamma();
gamma.setAlphaStatuses(new AlphaStatuses(Arrays.asList(new AlphaStatus("201612221122273660","201612221122273660"))));
ObjectMapper mapper=new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE,true);
String jsonString=mapper.writeValueAsString(gamma);
System.out.println("output "+jsonString);
}
**Output**:
output {"Gamma":{"alphaStatues":{"alphaStatus":[{"alphaHeaderKey":"201612221122273660","alphaLineKey":"201612221122273660"}]}}}
Related
I have JSON response which looks like that:
{
"response":[
"Some number (for example 8091)",
{
"Bunch of primitives inside the first JSONObject"
},
{
"Bunch of primitives inside the second JSONObject"
},
{
"Bunch of primitives inside the third JSONObject"
},
... (and so on)
]
}
So it's an array with first integer element and other elements are JSONObject.
I don't need integer element to be parsed. So how do I handle it using GSON?
I would solve this problem by creating a custom JsonDeserializer and registering it to your Gson instance before parsing. This custom deserializer would be set up to handle both ints and real objects.
First you need to build up a series of model objects to represent the data. Here's a template for what that might look like:
private static class TopLevel {
#SerializedName("response")
private final List<ResponseElement> elements;
private TopLevel() {
this.elements = null;
}
}
private static class ResponseInteger implements ResponseElement {
private final int value;
public ResponseInteger(int value) {
this.value = value;
}
}
private static class ResponseObject implements ResponseElement {
#SerializedName("id")
private final String id;
#SerializedName("text")
private final String text;
private ResponseObject() {
this.id = null;
this.text = null;
}
}
private interface ResponseElement {
// marker interface
}
TopLevel and ResponseObject have private constructors because they are going to let Gson set their fields using reflection, while ResponseInteger has a public constructor because we're going to manually invoke it from our custom deserializer.
Obviously you will have to fill out ResponseObject with the rest of its fields.
The deserializer is relatively simple. The json you posted contains only two kinds of elements, and we'll leverage this. Each time the deserializer is invoked, it checks whether the element is a primitive, and returns a ResponseInteger if so (or a ResponseObject if not).
private static class ResponseElementDeserializer implements JsonDeserializer<ResponseElement> {
#Override
public ResponseElement deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonPrimitive()) {
return new ResponseInteger(json.getAsInt());
}
else {
return context.deserialize(json, ResponseObject.class);
}
}
}
To use this deserializer, you'll have to register it with Gson using the GsonBuilder object.
private static Gson getGson() {
return new GsonBuilder()
.registerTypeAdapter(ResponseElement.class, new ResponseElementDeserializer())
.create();
}
And that's it. Now you can use this Gson object to easily parse TopLevel objects!
public void parseJson() {
TopLevel t = getGson().fromJson(json, TopLevel.class);
for (ResponseElement element : t.elements) {
System.out.println(element);
}
}
8061
[450602: Поздравляем!]
[451700: С реакцией чата и рассуждениями Папани после рипа..]
[451578: Помним...Любим...Скорбим...<br>2107 забирает лучших]
[451371: Земля тебе пухом братишка]
[451332: Доигрался, минус 900 экзов<br><br>R I P]
[451269: ]
[451242: https://www.twitch.tv/arthas подрубка<br><br>evilpapech.ru - скидка 30% на футболки!]
[451217: ]
[451181: или так це жерстко?]
[451108: ]
I used these toString() methods, which I omitted above for brevity:
#Override
public String toString() {
return Integer.toString(value);
}
#Override
public String toString() {
return "[" + id + ": " + text + "]";
}
Try this
Gson gson = new Gson();
// Reading from a file.
Example example = gson.fromJson(new FileReader("D:\\content.json"), Example.class);
POJO
package com.example;
public class Example {
private List<Integer> response = null;
public List<Integer> getResponse() {
return response;
}
public void setResponse(List<Integer> response) {
this.response = response;
}
}
Basically this structure is the wrong format for JSON data.
You need to remove the number, or put this number as a field in the same object like the one below (call ObjectA) and consider this is an array of ObjectA.
Then everything should work well. Try the code below:
public class Response {
#SerializedName("response")
#Expose
public List<ObjectA> objectA = null;
}
public class ObjectA {
#SerializedName("value")
#Expose
public Integer value;
#SerializedName("description")
#Expose
public String description;
}
Response response = new Gson().fromJson(responseString, Response.class);
Please use below ValueObject format which doesn't parse first integer element
public class ResponseVO {
public List<Response> response = new ArrayList();
public class Response {
public final long id;
public final long from_id;
...
}
}
I'm working on a project that communicates with an API using JSON. This is my first attempt at JSON and I've been away from java for a few/several years, so please bear with me.
Here is an idea of what the data looks like:
String 1:
[{
"apicall1":
[{
"thisField":"thisFieldData",
"thatField":"thatFieldData",
"anotherField":"anotherFieldData"
}]
}]
String 2:
[{
"apicall2":
[{
"thatField":"thatFieldData",
"someFieldsAreTheSame":"someFieldsAreTheSameData",
"otherFieldsAreNotTheSame":"otherFieldsAreNotTheSame"
}]
}]
As you can see from my data example, the API returns a JSON string that contains the api used. The array inside contains the data. The API's have a lot of data fields in common but they are unrelated beyond that.
EDIT: There are dozens of these API's types that will need to be handled.
What I am trying to do is create a response class that accepts all of the JSON strings and returns an object containing the appropriate data.
For Example:
Gson gson = new Gson(); //Custom TypeAdapter goes here if needed.
Response apicall2 = gson.fromJson(apicall2String, Response.class);
System.out.println(apicall2.thatField); //Prints thatFieldData
System.out.println(apicall2.someFieldsAreTheSame); //Prints someFieldsAreTheSameData
System.out.println(apicall2.otherFieldsAreNotTheSame); //Prints otherFieldsAreNotTheSameData
This is where I am lost. Here is what I have so far. I think I need to use a TypeAdapter here but haven't been able to figure how to apply that to my case.
public class Response { //Change to TypeAdapter possibly?
}
public class apicall1 {
String thisField;
String thatField;
String anotherField;
}
public class apicall2 {
String thatField;
String someFieldsAreTheSame;
String otherFieldsAreNotTheSame;
}
You can use Gson's TypeToken class to deserialize json into object. Below is an example:
JSON:
[{ "apicall1":
[{
"thisField":"thisFieldData",
"thatField":"thatFieldData",
"anotherField":"anotherFieldData"
}]
}]
Model:
class Response{
private List<Result> apicall1;
class Result{
private String thisField;
private String thatField;
private String anotherField;
public String getThisField() {
return thisField;
}
public void setThisField(String thisField) {
this.thisField = thisField;
}
public String getThatField() {
return thatField;
}
public void setThatField(String thatField) {
this.thatField = thatField;
}
public String getAnotherField() {
return anotherField;
}
public void setAnotherField(String anotherField) {
this.anotherField = anotherField;
}
}
public List<Result> getApicall1() {
return apicall1;
}
public void setApicall1(List<Result> apicall1) {
this.apicall1 = apicall1;
}
}
Converter:
public static void main(String[] args) {
String response = "[{ \"apicall1\": [{ \"thisField\":\"thisFieldData\", \"thatField\":\"thatFieldData\", \"anotherField\":\"anotherFieldData\" }]}]";
Gson gson = new Gson();
List<Response> responses = gson.fromJson(response, new TypeToken<List<Response>>(){}.getType());
System.out.println(responses.get(0).getApicall1().get(0).getThisField());
}
I don't know if you want both adapters in one class. Might not be the best OOP design.
To achieve it you would need to do something like so:
public class DoublyTypeAdapter implements JsonDeserializer<ApiCallTypeParent>
{
Gson gson = new Gson();
#Override
public ApiCallTypeParent deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext)
throws JsonParseException {
JsonObject json = jsonElement.getAsJsonObject();
ApiCallTypeParent desrializeIntoMe;
// Detect which type to implement
if(apiTypeOne(type) {
desrializeIntoMe = new TypeOne();
} else {
desrializeIntoMe = new TypeTwo();
}
for (Map.Entry<String, JsonElement> entry : json.entrySet())
{
switch(entry.getKey()){
case "thisField":
desrializeIntoMe.setThisField(entry.getValue().getAsString());
break;
......
default: // We don't care
break;
}
}
return desrializeIntoMe ;
}
}
I have this json:
[{"cdCondicaoPagto":"1","NrParcela":1,"NrDias":0}]
and this class:
public static class CondicaoPagtoItem implements Serializable {
private String cdCondicaoPagto;
private Integer NrParcela;
private Integer NrDias;
public CondicaoPagtoItem() {
}
public String getCdCondicaoPagto() {
return cdCondicaoPagto;
}
public void setCdCondicaoPagto(String cdCondicaoPagto) {
this.cdCondicaoPagto = cdCondicaoPagto;
}
public Integer getNrParcela() {
return NrParcela;
}
public void setNrParcela(Integer NrParcela) {
this.NrParcela = NrParcela;
}
public Integer getNrDias() {
return NrDias;
}
public void setNrDias(Integer NrDias) {
this.NrDias = NrDias;
}
}
And I'm trying to read it by streaming, this way:
JsonFactory jsonFactory = new JsonFactory();
ObjectMapper jsonMapper = new ObjectMapper(jsonFactory);
JsonNode jsonNodeGeral = jsonMapper.readTree(new File("/home/cechinel/Documentos/CondicaoPagtoItem.json"));
Iterator<JsonNode> elements = jsonNodeGeral.getElements();
while(elements.hasNext()){
JsonNode jsonNode = elements.next();
CondicaoPagtoItem condicao = jsonMapper.treeToValue(jsonNode, CondicaoPagtoItem.class);
}
But It causing the following error:
UnrecognizedPropertyException: Unrecognized field "NrParcela"
If I use the annotation #JsonProperty it works, but I don't want to do it in which integer field.
It sounds to me more like it's a naming convention mismatch. setNrParcela would map to a field name nrParcela but your JSON document has the 'n' capitalized as NrParcela.
If you cannot change the JSON field capitalization, you can use #JsonProperty with an overridden name:
#JsonProperty("NrParcela")
But since you didn't want to do that, another option to consider is implementing a PropertyNamingStrategy.
I have a JSON feed which I am trying to parse:
{
"test": [
"5",
{
"data": "someData",
"number": "9"
},
{
"data": "someData",
"number": "9"
}
]
}
I am using Jackson to parse the JSON file for me and using annotations:
private ArrayList<Test> test = new ArrayList<Test>();
/**
* #param test the test to set
*/
public void setTest(ArrayList<Test> test) {
this.test = test;
}
And my Test class:
class Test{
private String data= "";
private String number= "";
/**
* #return the data
*/
public String getData() {
return data;
}
/**
* #param data the data to set
*/
public void setData(String data) {
this.data= data;
}
/**
* #return the number
*/
public String getNumber() {
return number;
}
/**
* #param number the number to set
*/
public void setNumber(String number) {
this.number= number;
}
}
If I remove the 5 from my JSON, it works fine. However the 5 will be in the feed so I need a way to parse it. I'm completely new to jackson and annotations in general and after spending most of the day trying to figure this out without any luck, I need some help!
How can I ignore the "5" if its not named, I've tried creating a container class which holds a String and the ArrayList and passed that as a paramater to the setTest method but that didnt work either.
Your Java Test class represents the object, {"data": "someData", "number": "9"}, in the array. You will need to define another Java class to map your Test JSON object, something like
class TopLevel {
private int count;
List<Test> testList = new ArrayList<Test>();
// ...
//setters and getters
}
You can write custom deserializer for this list:
class TestListDeserializer extends JsonDeserializer<List<Test>> {
#Override
public List<Test> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
List<Test> result = new ArrayList<Test>();
while (jp.nextToken() != JsonToken.END_ARRAY) {
if (jp.getCurrentToken() == JsonToken.START_OBJECT) {
result.add(jp.readValueAs(Test.class));
}
}
return result;
}
}
Now, you have to annotated test property:
#JsonDeserialize(using = TestListDeserializer.class)
private List<Test> test;
You can do it this way
String jsonStr ="{\"test\": [\"5\",{ \"data\": \"someData\", \"number\": \"9\"},
{ \"data\": \"someData\",\"number\": \"10\"}]}";
JSONObject jsonObject =new JSONObject(jsonStr);
String jsonArr = jsonObject.get("test").toString();
JSONArray jsonArray = new JSONArray(jsonArr);
for(int i =1 ;i<jsonArray.length();i++){
JSONObject object = new JSONObject(jsonArray.get(i).toString());
System.out.println(object.get("data")); // use setter instead
System.out.println(object.get("number")); // use setter instead
}
{
"TestSuite":{
"TestSuiteInfo":{
"-description":"parse"
},
"TestCase":[
{
"TestCaseData":{
"-sequence":"sequential",
"-testNumber":"2",
"-testCaseFile":"testcase\\Web\\Ab.xml"
}
},
{
"TestCaseData":{
"-sequence":"sequential",
"-testNumber":"3",
"-testCaseFile":"testcase\\Web\\BC.xml"
}
}
]
}
}
My Pojos are:
public class TestSuite {
private TestSuiteInfo testSuiteInfo;
private TestCase listOfTestCases;
public TestSuiteInfo getTestSuiteInfo() {
return testSuiteInfo;
}
public void setTestSuiteInfo(TestSuiteInfo testSuiteInfo) {
this.testSuiteInfo = testSuiteInfo;
}
public TestCase getListOfTestCases() {
return listOfTestCases;
}
public void setListOfTestCases(TestCase listOfTestCases) {
this.listOfTestCases = listOfTestCases;
}
}
public class TestSuiteInfo {
private String description;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
import java.util.Iterator;
import java.util.List;
public class TestCase {
private List<TestCaseData> testCaseData;
public List<TestCaseData> getTestCaseData() {
return testCaseData;
}
public void setTestCaseData(List<TestCaseData> testCaseData) {
this.testCaseData = testCaseData;
}
}
public class TestCaseData {
private String sequence;
private int testNumber;
private String testCaseFile;
public String getSequence() {
return sequence;
}
public void setSequence(String sequence) {
this.sequence = sequence;
}
public int getTestNumber() {
return testNumber;
}
public void setTestNumber(int testNumber) {
this.testNumber = testNumber;
}
public String getTestCaseFile() {
return testCaseFile;
}
public void setTestCaseFile(String testCaseFile) {
this.testCaseFile = testCaseFile;
}
}
I haven't use Jackson before, will really appreciate if anyone could help me in parsing the file and getting the objects.
I am trying to parse this from past two days, but didnt got any success
Usually to parse JSON with the Jackson library, you would use the ObjectMapper class like this:
public static void main(final String[] args) {
final String json = "some JSON string";
final ObjectMapper mapper = new ObjectMapper();
final TestSuite readValue = mapper.readValue(json, TestSuite.class);
//Then some code that uses the readValue.
//Keep in mind that the mapper.readValue() method does throw some exceptions
//So you'll need to handle those too.
}
However, I wrote a quick test class to check out the parsing of your JSON and came across some issues.
Basically, the design of the JSON and the design of the domain don't match up. So you can either alter the JSON, or you can alter the domain objects.
Altering the JSON to fit the domain
The property names that have "-" in them wont parse nicely in jackson, so they will need to be removed.
Having the class name before eachof the objects isn't going to help. Jackson will expect these to be properties, so the Class names will need removing or replacing with property names.
Property names must be provided as they are in the domain objects in order for jackson to parse them. You can't just say here's an object and then start a list, the list must have a property name/
After I'd adjusted a these things in the JSON, I got it to parse with the provided domain objects. The JSON I ended up with looked like this:
{
"testSuiteInfo":{
"description":"parse"
},
"listOfTestCases":{
"testCaseData":[
{
"sequence":"sequential",
"testNumber":"2",
"testCaseFile":"testcase\\Web\\Ab.xml"
},
{
"sequence":"sequential",
"testNumber":"3",
"testCaseFile":"testcase\\Web\\BC.xml"
}
]
}
}
Here's my test method that does parse the doctored JSON above (please ignore all the escape characters)
public static void main(final String[] args) {
final String json = "{\"testSuiteInfo\":{\"description\":\"parse\"}," +
"\"listOfTestCases\":{" +
"\"testCaseData\":[" +
"{\"sequence\":\"sequential\",\"testNumber\":\"2\",\"testCaseFile\":\"testcase\\\\Web\\\\Ab.xml\"}," +
"{\"sequence\":\"sequential\",\"testNumber\":\"3\",\"testCaseFile\":\"testcase\\\\Web\\\\BC.xml\"}" +
"]" +
"}" +
"}";
final ObjectMapper mapper = new ObjectMapper();
try {
final TestSuite readValue = mapper.readValue(json, TestSuite.class);
System.out.println(readValue.getListOfTestCases()); //just a test to see if the object is built
}
catch (final Exception e) {
e.printStackTrace();
}
}
Altering the domain to fit the JSON
Firstly, the main issues is having the Class names as the property identifiers. That makes it quite difficult to work with this JSON in the usual manner. I've had to add a couple of wrapper classes to get around the class names being in the JSON.
I've added an OverallWrapper class that has a TestSuite property to cater for the TestSuite class name in the JSON.
I've also added a TestCaseDataWrapper class to cater for the TestCaseData class names in the list in the JSON.
I removed the TestCase class all together as that just became a property on one of the other classes.
Then to make the property names match up with the objects, I've used the #JsonProperty annotation.
Here are the classes after the modifications, and the ultimate parser test method that works and parses the JSON. (again, excuse all the escape characters in the JSON string)
import org.codehaus.jackson.annotate.JsonProperty;
public class OverallWrapper {
private TestSuite testSuite;
#JsonProperty("TestSuite")
public TestSuite getTestSuite() {
return this.testSuite;
}
public void setTestSuite(final TestSuite testSuite) {
this.testSuite = testSuite;
}
}
import java.util.List;
import org.codehaus.jackson.annotate.JsonProperty;
public class TestSuite {
private TestSuiteInfo testSuiteInfo;
private List<TestCaseDataWrapper> testCaseData;
#JsonProperty("TestCase")
public List<TestCaseDataWrapper> getTestCaseData() {
return this.testCaseData;
}
public void setTestCaseData(final List<TestCaseDataWrapper> testCaseData) {
this.testCaseData = testCaseData;
}
#JsonProperty("TestSuiteInfo")
public TestSuiteInfo getTestSuiteInfo() {
return this.testSuiteInfo;
}
public void setTestSuiteInfo(final TestSuiteInfo testSuiteInfo) {
this.testSuiteInfo = testSuiteInfo;
}
}
import org.codehaus.jackson.annotate.JsonProperty;
public class TestSuiteInfo {
private String description;
#JsonProperty("-description")
public String getDescription() {
return this.description;
}
public void setDescription(final String description) {
this.description = description;
}
}
import org.codehaus.jackson.annotate.JsonProperty;
public class TestCaseDataWrapper {
#JsonProperty("TestCaseData")
private TestCaseData testcaseData;
public TestCaseData getTestcaseData() {
return this.testcaseData;
}
public void setTestcaseData(final TestCaseData testcaseData) {
this.testcaseData = testcaseData;
}
}
import org.codehaus.jackson.annotate.JsonProperty;
public class TestCaseData {
private String sequence;
private int testNumber;
private String testCaseFile;
#JsonProperty("-sequence")
public String getSequence() {
return this.sequence;
}
public void setSequence(final String sequence) {
this.sequence = sequence;
}
#JsonProperty("-testNumber")
public int getTestNumber() {
return this.testNumber;
}
public void setTestNumber(final int testNumber) {
this.testNumber = testNumber;
}
#JsonProperty("-testCaseFile")
public String getTestCaseFile() {
return this.testCaseFile;
}
public void setTestCaseFile(final String testCaseFile) {
this.testCaseFile = testCaseFile;
}
}
public static void main(final String[] args) {
final String json = "{\"TestSuite\":{\"TestSuiteInfo\":{\"-description\":\"parse\"},\"TestCase\":[" +
"{\"TestCaseData\":{\"-sequence\":\"sequential\",\"-testNumber\":\"2\",\"-testCaseFile\":\"testcase\\\\Web\\\\Ab.xml\"}}," +
"{\"TestCaseData\":{\"-sequence\":\"sequential\",\"-testNumber\":\"3\",\"-testCaseFile\":\"testcase\\\\Web\\\\BC.xml\"}}" +
"]}}";
final ObjectMapper mapper = new ObjectMapper();
try {
final OverallWrapper readValue = mapper.readValue(json, OverallWrapper.class);
System.out.println(readValue.getTestSuite());
}
catch (final Exception e) {
e.printStackTrace();
}
}
Summing up
The ultimate issue is that the domain doesn't marry up with the JSON.
Personally I prefer to change the JSON to marry up to the domain, as the domain seems to make sense in it's design and requires less customization and forcing.
However, I do accept that you may not have that choice, hence the redesign of the domain.
In this blog you can find a simple way to parse a large json file without directly using Jackson's ObjectMapper
https://www.ngdata.com/parsing-a-large-json-file-efficiently-and-easily/
With jp.skipChildren() and nested loops you can reach to your section of interest and once you are there simply break the nested loops using a label:
outerloop: while (jp.nextToken() != JsonToken.END_OBJECT) {
//...nested loops here
break outerloop;
//...closing loops
}
I copied the code for reference:
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.*;
import java.io.File;
public class ParseJsonSample {
public static void main(String[] args) throws Exception {
JsonFactory f = new MappingJsonFactory();
JsonParser jp = f.createJsonParser(new File(args[0]));
JsonToken current;
current = jp.nextToken();
if (current != JsonToken.START_OBJECT) {
System.out.println("Error: root should be object: quiting.");
return;
}
while (jp.nextToken() != JsonToken.END_OBJECT) {
String fieldName = jp.getCurrentName();
// move from field name to field value
current = jp.nextToken();
if (fieldName.equals("records")) {
if (current == JsonToken.START_ARRAY) {
// For each of the records in the array
while (jp.nextToken() != JsonToken.END_ARRAY) {
// read the record into a tree model,
// this moves the parsing position to the end of it
JsonNode node = jp.readValueAsTree();
// And now we have random access to everything in the object
System.out.println("field1: " + node.get("field1").getValueAsText());
System.out.println("field2: " + node.get("field2").getValueAsText());
}
} else {
System.out.println("Error: records should be an array: skipping.");
jp.skipChildren();
}
} else {
System.out.println("Unprocessed property: " + fieldName);
jp.skipChildren();
}
}
}
}
From the blog:
The nextToken() call each time gives the next parsing event: start object, start field, start array, start object, …, end object, …, end array, …
The jp.skipChildren() is convenient: it allows to skip over a complete object tree or an array without having to run yourself over all the events contained in it.
All the credits go to the blog's author: Molly Galetto