I have the following json
{ "file": {"file": "foo.c", "owner": "user123"}
"methods": [{"name": "proc1", "value":"val"}, {"name":"proc2","value":"val2"}]
etc...
}
I know that I can do something like
class file{
public String file
public String owner
}
class methods{
public String name
public String value
}
and I can either call
File file= gson.fromJson(jsonInString, File.class);
methods[] array = gson.fromJson(jsonInString, methods[].class);
but what do I do if I need to handle a complex json that contains many objects all togther
I cannot specify gson.fromJson(jsonInString, ListOfClasses)
I normally follow this approach to get any complex classes converted from json to object. This approach works for almost everything like list, map etc. The idea is simple create holders for the complex classes and then create the classes. Give as much depth as much required. The trick is to match name in Json and your holders (and subclasses).
File Config:
class FileConfig{
public String file;
public String owner;
//define toString, getters and setters
}
Method Class:
class Method{
public String name;
public String value;
//define toString, getters and setters
}
Method Config:
class MethodConfig{
List<Method> methods = null;
//define toString, getters and setters
}
Holding the Config:
public class HolderConfig {
private FileConfig file = null;
private MethodConfig methods = null;
public FileConfig getFile() {
return file;
}
public void setFile(FileConfig file) {
this.file = file;
}
public MethodConfig getMethods() {
return file;
}
public void setMethods(MethodConfig methods) {
this.methods = methods;
}
}
Building the config:
public class HolderConfigBuilder {
public static HolderConfig build(JsonObject holderConfigJson) {
HolderConfig configHolderInstance = null;
Gson gsonInstance = null;
gsonInstance = new GsonBuilder().create();
configHolderInstance = gsonInstance.fromJson(holderConfigJson,HolderConfig.class);
return configHolderInstance;
}
}
Demo class:
public class App
{
public static void main( String[] args )
{
HolderConfig configHolderInstance = null;
FileConfig file = null;
configHolderInstance = HolderConfigBuilder.build(<Input Json>);
file = configHolderInstance.getFile();
System.out.println("The fileConfig is : "+file.toString());
}
}
Input Json:
{ "file": {"file": "foo.c", "owner": "user123"}
"methods": [
{"name": "proc1", "value":"val"},
{"name":"proc2","value":"val2"}
]
}
Note: Write the code to get Input JSON in your test code.
In this way whenever you add more elements to your JSON you have to create a separate class for that element and just add the element name same as in your json into the HolderConfig. You need not change rest of the code.
Hope it helps.
Related
I'm working on a RESTful API project and I'm using JAVA to accomplish the endpoints creation, because the environment where I'm working is an IBM Domino database. I'm using the org.json jar to create the objects and provide all the responses, but now I would modify the project, working directly with Java classes, because it's becoming bigger and bigger... By the way I have problems with nested Java objects.
Basically I have the classes LabelValue, Content and ResultsBlock instantiated in another class that set all the required fields and the generate a JSONObject calling its constructor plus the new object. When I'm doing this I have a Null pointer exception so the system doesn't provide any response.
I think that the problem is with the declaration, in the class Resultblock, of the nested Content object. But I don't know how can I manage this kind of situation. Can you help me?
When I'm working with easier classes where the attributes are the generical data types and when I create the ArrayList of type Content I've no problem and everything works well.
Thanks in advance!
p.s. I can't use gson library because it creates a java policy problem with IBM Domino server.
public class LabelValue implements java.io.Serializable{
private String label;
private String value;
public void setLabel(String label) {
this.label = label;
}
public void setValue(String value) {
this.value = value;
};
}
public class Content implements java.io.Serializable{
private String title;
private String description;
private ArrayList <LabelValue> totals;
public void setTotals(ArrayList<LabelValue> totals) {
this.totals = totals;
}
public void setTitle(String title) {
this.title = title;
}
public void setDescription(String description) {
this.description = description;
}}
public class ResultsBlock implements java.io.Serializable{
private String type;
private Content content;
public void setType(String type) {
this.type = type;
}
public void setContent(Content content) {
this.content = content;
}}
in the main :
{
Content content = new Content();
content.setTitle("title");
content.setDescription("description");
content.setTotals(label);
ResultsBlock result = new ResultsBlock ();
result.setContent(content);
result.setType("result");
return new JSONObject(result);}
this is the expected output of the blockResult class:
"blocks":
{
"type": "result",
"content": {
"title": "title",
"description": "description",
"totals": [
{
"label": "label",
"value": value
}
]
}
}
If I understand correctly what you need, I would suggest you to use Jackson if you can't use GSON.
I tried to make a test with the code you provided and, after adding all get methods to your POJO classes, I implemented this example:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class MainClass {
public static void main(String[] args) throws JsonProcessingException {
Content content = new Content();
content.setTitle("title");
content.setDescription("description");
ResultsBlock result = new ResultsBlock ();
result.setContent(content);
result.setType("result");
// JSONObject(result);
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(result);
System.out.println(jsonString);
}
}
It prints out the following:
{
"type": "result",
"content": {
"title": "title",
"description": "description",
"totals": null
}
}
Now you can access your JSON as you like.
Of course I've not considered all fields for simplicity.
Hope this helps.
I am trying to generate the following using fasterxml jackson.. but I am stuck. I can't seem to work out how to create arrays.
{
"setAccId":"12345",
"groupOf":null,
"isEnabled":false,
"list":[
{
"student":"jim",
"type":"S_A",
"retro":null
},
{
"student":"bob",
"type":"S_A",
"retro":null
}
],
"sort":[]
}
I have two classes. One has the Json properties, and the other is where I print it.
Below class (DynamicJsonHelper) is where I have all the json properties
package com.company.jsonfc;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonPropertyOrder({
"accId",
"groupOf",
"isEnabled"
})
public class DynamicJsonHelper {
public String accId;
public String groupOf;
public List studentList;
#JsonProperty("accId")
public void setAccId(String accId) {
this.accId = accId;
}
#JsonProperty("groupOf")
public void setGroupOf(String groupOf) {
this.groupOf = groupOf;
}
#JsonProperty("isEnabled")
public boolean isEnabled() {
return false;
}
#JsonProperty("studentList")
public List<StudentList> studentList() {
return studentList;
}
}
Student List Class (as suggested)
class StudentList {
String student;
String type;
String retro;
}
And here is class (PrintJson) where I call it.
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.company.jsonfc.DynamicJsonHelper;
#JsonInclude(JsonInclude.Include.NON_NULL)
public class PrintJson {
#Test
public void create_json() throws JsonProcessingException {
final JsonNodeFactory factory = JsonNodeFactory.instance;
DynamicJsonHelper dynamicJsonHelper = new DynamicJsonHelper();
String jsonString;
ObjectMapper mapper = new ObjectMapper();
dynamicJsonHelper.setAccId("12345");
jsonString = mapper.writeValueAsString(dynamicJsonHelper);
System.out.println(jsonString);
}
}
This results in printing the following:
{
"setAccId":"12345",
"groupOf":null,
"isEnabled":false
"studentList":null
}
1) How do I add the list:[ ... ] array & the sort: [ ] at the end too?
2) In class PrintJson, I don't set value for groupOf but it is still created in Json. How do I set it so if I set value, it is printed.. otherwise it is not included in the json being printed.
I would appreciate if you took my code and gave me example based on it for better understanding please
1) How do I add the list:[ ... ] array & the sort: [ ] at the end too?
Answer: You can create two more classes one for list and one for sort. Now in the class DynamicJsonHelper you can add both of them like you have added accId or isEnabled
and they will be printed. Make sure to add the fields you want in them as instance variables in both of the classes. For example for list you can have a class like:
class StudentList{
String student;
String type;
String retro;
}
Now add a field in your class DynamicJsonHelper as List<StudentList>. Similarly you can do for sort.
2) In class PrintJson, I don't set value for groupOf but it is still
created in Json. How do I set it so if I set value, it is printed..
otherwise it is not included in the json being printed.
Answer: You can either use Object mapper and set it to ignore the null fields during serialization. For eg: mapper.setSerializationInclusion(Include.NON_NULL);
Or you can set it at class level to ignore null values if any. For eg:
#JsonInclude(Include.NON_NULL)
class Test
{
String t;
}
As mentioned in the comments by aBnormaLz above doesn't work if the type is primitive like you have for isEnabled. So consider changing it to Boolean and ensure the same for other fields also.
Edit:
#JsonPropertyOrder({
"accId",
"groupOf",
"isEnabled"
})
#JsonInclude(JsonInclude.Include.NON_NULL)
public class DynamicJsonHelper {
public String accId;
public String groupOf;
public List<Student> studentList;
#JsonProperty("accId")
public void setAccId(String accId) {
this.accId = accId;
}
#JsonProperty("groupOf")
public void setGroupOf(String groupOf) {
this.groupOf = groupOf;
}
#JsonProperty("isEnabled")
public boolean isEnabled() {
return false;
}
#JsonProperty("studentList")
public void setStudentList(List<Student> list) {
this.studentList = list;
}
}
class Student {
private String student;
private String type;
private String retro;
public Student(String student, String type, String retro) {
this.student = student;
this.type = type;
this.retro = retro;
}
public String getStudent() {
return student;
}
public void setStudent(String student) {
this.student = student;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getRetro() {
return retro;
}
public void setRetro(String retro) {
this.retro = retro;
}
}
class HelperTest{
public static void main(String[] args) throws JsonProcessingException {
DynamicJsonHelper dynamicJsonHelper = new DynamicJsonHelper();
String jsonString;
ObjectMapper mapper = new ObjectMapper();
dynamicJsonHelper.setAccId("12345");
List<Student> list = Arrays.asList(new Student("s1", "t1", "r1"), new Student("s2", "t2", "r2"));
dynamicJsonHelper.setStudentList(list);
jsonString = mapper.writeValueAsString(dynamicJsonHelper);
System.out.println(jsonString);
}
}
After executing the program the output is as shown below:
{
"accId": "12345",
"isEnabled": false,
"studentList": [
{
"student": "s1",
"type": "t1",
"retro": "r1"
},
{
"student": "s2",
"type": "t2",
"retro": "r2"
}
]
}
You missed a point that you have to have third class and it must have property of type java.util.List or array and name list for your example.
e.g.
public class JsonHolder {
// appropriate Json/Jackson annotations ommitted
private String setAccId;
private String groupOf;
private boolean isEnabled;
private List<DynamicJsonHelper> list;
private String[] sort;
// .. getter/setters ...
}
then you have to create that object and when you created your DynamicJsonHelper put it into list or array.
After all you can serialize JsonHolder object and you will see your java List or Array as JSON array.
UPD: just note that in JSON structure like { ... } is an object and in Java there must be a class for it.
So, starting with JSON structure you posted
{
"setAccId":"12345",
"groupOf":null,
"isEnabled":false,
"list":[
{
"student":"jim",
"type":"S_A",
"retro":null
},
{
"student":"bob",
"type":"S_A",
"retro":null
}
],
"sort":[]
}
it is an object (let name it JsonHolder) with properties named setAcctId, groupOf, isEnabled, list, sort
So you have to have a Java class for it (similar as you did for your DynamicJsonHelper. You can use any #Json annotations you'd like to the same way (I omitted them and left for you). Even you will not have them Jackson or any other JSON serialiser will use property names in Java class by as default.
I mean as example your #JsonProperty("accId")
#JsonProperty("accId")
public void setAccId(String accId)
is not required as long as getter/setter/property named also getAcctId, setAcctId, acctId. Jackson will use that if there is no #JsonProperty annotation.
BTW it is better to do it as you did for code readability. :-)
Per your question:
list and sort properties in JSON are arrays. Jackson parses Java collections classes like List, Set or Arrays to JSON arrays.
Then according to required JSON structure list property is a such collection of DynamicJsonHelper objects you created. in Java class, List or Array is just your choice - use what is more suitable for you. I recommend to use a List rather than Array. Work with arrays in Java is not a good idea. :-)
So far you just created a Java class DynamicJsonHelper only for objects which must be in list property of JsonHolder object. What's left is to create that "JsonHolder" class and give that object to Jackson to serialize it into required JSON structure.
There are bunch of #Json annotations you can use to allow or not null or empty values, change property names, exclude Java class properties to be serialized and so, and so... All is up to you... good luck!
Input Json file looks like below code:
{
"Payload": {
"any0": {
"pricingResponse": {
"priceDetails": {
"priceDetails": {
"seqId": "8015B000000Umo1QAC-",
"companyCode": "EPA",
"priceType": "Fuel Price",
"priceRequestSource": ""
},
"priceLineItems": {
"lineItem": [
{
"lineItemInfo": {
"seqId": "8015B000000Umo1QAC-28052018105047-022596",
"orderId": "00000266",
"lineItemId": "44",
"itemId": "70051",
"quantity": "100.00",
"unitPrice": "2.03200",
"unitPriceLCY": "2.03200"
}
}
]
}
}
}
}
}
}
Expected output(java class)
public class Payload
{
public Any0 any0;
}
/* Stub Class for Any0 */
public class Any0
{
public PricingResponse pricingResponse;
}
/* Stub Class for PricingResponse */
public class PricingResponse
{
public PriceDetails priceDetails;
}
/* Stub Class for PriceDetails */
public class PriceDetails
{
public String seqId;
public String priceRequestSource;
public String companyCode;
public String priceType;
public String deliveryType;
public String currencyCode;
public String priceDate;
public String applyOrderQuantity;
public String totalOrderQuantity;
public String customerId;
public String shipToId;
public String supplyLocationId;
public String transporterId;
public String onRun;
public String orderId;
public PriceDetails priceDetails;
public PriceLineItems priceLineItems;
public Error Error;
}
/* Stub Class for PriceLineItems */
public class PriceLineItems
{
public String orderId;
public List<LineItem> lineItem;
}
/* Stub Class for LineItem */
public class LineItem
{
public LineItemInfo lineItemInfo;
public AccountingDetails accountingDetails;
public Error Error;
}
I tried to convert it using Jackson Library it's working and creating as the separate POJO classes but I want to create nested class.This is my sample code
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);
}
This is the output I am getting
Is there a possible way to convert dynamic nested Json class file into Java class? Any help would be appreciated.
Use a Map<String, ValueClass> for every structure that contains variable keys and unvarying values (of type ValueClass).
If you'd also like to deserialize maps with variable value structures, you'll need to write a custom deserializer that can infer which Java class should the JSON be deserialized to. Here's a tutorial for how can you do that with GSON.
Currently I have form like below:
public class Form {
private String listOfItems;
public String getListOfItems() {
return listOfItems;
}
public void setListOfItems(String listOfItems) {
this.listOfItems= listOfItems;
}
}
For instanse listOfItems equals to the following string "1,2,3".
The goal is to serialize this form to following format:
{
"listOfItems": [1, 2, 3]
}
It would be good to know how to correctly do such thing? As I know it is possible to create some custom serializer then mark appropriate getter method with it, like this #JsonSerialize(using = SomeCustomSerializer).
But not sure whether it is correct approach, probably any default implementations already exist.
If you can edit your Form class:
public class Form {
private String listOfItems;
public String getListOfItems() {
return listOfItems;
}
public void setListOfItems(String listOfItems) {
this.listOfItems = listOfItems;
}
#JsonProperty("listOfItems")
public List<Integer> getArrayListOfItems() {
if (listOfItems != null) {
List<Integer> items = new ArrayList();
for (String s : listOfItems.split(",")) {
items.add(Integer.parseInt(s)); // May throw NumberFormatException
}
return items;
}
return null;
}
}
By default Jackson looks for getters for serializing. You can override this by using #JsonProperty annotation.
ObjectMapper mapper = new ObjectMapper();
Form form = new Form();
form.setListOfItems("1,2,3");
System.out.print(mapper.writeValueAsString(form));
Outputs:
{"listOfItems":[1,2,3]}
In my Android app I have json, which looks like :
{
"Records": [
{
"RowIndex": "0",
"NameValue": {
"Name": "PropertyName1",
"Value": "PropertyValue1"
}
}{
"RowIndex": "1",
"NameValue": {
"Name": "PropertyName2",
"Value": "PropertyValue2"
}
}
]
}
I need to parce this json to object, which looks like:
public class MyClass {
public String PropertyName1;
public String PropertyName2;
}
And result after parsing should be:
public String PropertyName1 = "PropertyValue1";
public String PropertyName2 = "PropertyValue2";
Basically, the first json is equivalent of:
{
"PropertyName1" : "PropertyValue1",
"PropertyName2" : "PropertyValue2"
}
Question: How can I parce first json without usage swith/case to search for the necessary Property?
You'll have to go down the dark path of reflection I'm afraid.
you can parse the json into an intermediary object which has a map for namevalue.
then you use the below code (ofcourse just copy paste the bits you need) to loop over the map of key/value pairs. for each key look up the field you want, and set it. If you're guaranteed only to need to set public variables then you can use getFields and can skip the setAccessible.
public class Test {
public static void main(String[] argv) {
MyClass myClass = new MyClass();
Class<?> classObject = myClass.getClass();
// Field fields[] = classObject.getFields(); // if you want to get only public fields.
Field fields[] = classObject.getDeclaredFields(); // any field
for(Field f : fields) {
System.out.println(f.getName());
try {
// if member is private: security managers may object but the default java allows it
f.setAccessible(true);
f.set(myClass, "abc");
} catch (IllegalAccessException e) {
// handle access exception:
e.printStackTrace();
}
}
System.out.println("prop 1: " + myClass.PropertyName1);
System.out.println("prop 2: " + myClass.PropertyName2);
}
public static class MyClass {
public String PropertyName1;
private String PropertyName2;
}
}
Actually.. there is a non-reflect way but that will replace your implementation of the object you have.
If you change your class:
public class MyClass {
public String PropertyName1;
public String PropertyName2;
}
to
public class MyClass {
private Map<String, String> properties = new HashMap<String, String>();
public void setProperties(Map<String, String> props) { this.properties = props; }
public String getPropertyName1() {
return lookupProperty("PropertyName1");
}
public String getPropertyName2() {
return lookupProperty("PropertyName2");
}
private String lookupProperty(String property) {
if (properties.containsKey(property) {
return properties.get(property);
} else {
return null;
}
}
}
then you could parse the name value map into a map, and construct a myclass with it.
just listing it for completeness, though changing your domain model to fit a json input is not ideal.
I would recommend either way to do the input parsing, and then copy over the model into your actual domain object rather than using the json-model in your application. that way if the json model ever changes, your domain model will not change.
One method I can think of (which doesn't sound too great) is to actually make an object that matches the JSON response you get back. Then, map THAT NameValue object to MyClass
So, something like
public class NameValue {
public string Name;
public String Value;
public MyClass getMyClass(){
MyClass myClass = new MyClass();
myClass.PropertyName2 = Value;
return myClass;
}
}
You can come up with a better way to map it, obviously. But this is just an example of something I might do if I was given a response JSON I didn't particularly care for. You can similarly reverse it (have MyClass be able to create a NameValue object) so you can send data back in the correct format.