I want to create a model from JSON where key is a value. This is the exact issue described, but in iOS. I want a similar solution in Android. Basically I want to have a Decodable equivalent in Android.
I am using GSON to parse JSON to model. As of now, I have compared the JSON key (mentioned in the link) against static values.
JSON :
{
"rows" :
[
{
"_id": "5cdc0ede5c3dcb04bdb3a972",
"emp_code": 187,
"log_id": 361711,
"punch_time": "2019-05-07T04:00:33.000Z",
"pin_type": 1,
"status": 4,
"__v": 0
},
{
"_id": "5cdc40de5c3dcb04bdb3a972",
"emp_code": 111,
"log_id": 361701,
"punch_time": "2019-05-07T04:00:35.000Z",
"pin_type": 101,
"status": 4,
"__v": 0
}
],
"pin_type_text": {
"1": "In Fingerprint",
"4": "In Card",
"101": "Out Fingerprint",
"104": "Out Card"
}
}
The value of pin_type in each row refers to the record in pin_type_text mapped with it's key.
I am using GSON for creating models, and here is the model class :
class MyModel {
var length : Long = 0
var rows = ArrayList<Rows>()
var pin_type_text : String = ""
}
class PinTypeText {
var 1 : String = ""
var 4 : String = ""
var 101 : String = ""
var 104 : String = ""
}
Basically, the keys defined in class PinTypeText are the values of the key 'pin_type' obtained in Rows model as seen in the JSON shared. So in this case, the keys in 'PinTypeText' are dependent on values defined in 'rows'. Hence, i want 'PinTypeText' model to be created with respect to 'pin_type' values from 'Rows' model.
Issue : Suppose in future, the 'pin_type' values - 1, 4, 101, 104 change in the backend, how can I handle such a case without changing my model. As per this model structure, I need to change my model class every time the backend model changes
you can store the item PinTypeText as a JsonElement and not as a custom class,
so your response model will be something like this
public class Response{
#SerializedName("rows")
#Expose
private List<Row> rows = null;
#SerializedName("pin_type_text")
#Expose
private JsonElement pinTypeText;
public List<Row> getRows() {
return rows;
}
public void setRows(List<Row> rows) {
this.rows = rows;
}
public JsonElement getPinTypeText() {
return pinTypeText;
}
public void setPinTypeText(JsonElement pinTypeText) {
this.pinTypeText = pinTypeText;
}
}
and when you want to check the type you can convert it to JsonObject and get the value for the key,
example
pinTypeText= response.getPinTypeText().asJsonObject().get([your pin_type here]).toString()
Related
I'm passing a json to objectmapper. The JSON string looks like this:
{
"id": "ID1",
"identifier" : "XYZ",
"data": [
{
"id": "sampleParentID",
"childrenElements": [
{
"id" : "sampleChildID",
"content" : "sample child content",
}
]
}
]
}
val objectMapper = ObjectMapper().registerModule(KotlinModule())
val obj: Object1 = objectMapper.readValue(jsonString)
My class looks something like this :
data class Object 1 (
var id : String? = null,
var identifier : String? = null,
var data: MutableList<Element>? = null,
){
// some functions
}
class Element (
var id : String?= null
var content : String? = null
var children: List<Element>? = listOf(),
) {
// som functions
}
From obj, data field is nested which is an object itself.
I want to get hashCode of data so I do obj.data.hashCode(). Let's say 12345 gets generated.
I store this in the database. Now, let's say the user sends another request with exactly the same JSON, again the JSON gets converted into an object from which I extract the data field and now when I do obj.data.hashCode(), 12345 is not generated, rather some other number gets generated.
Is this behavior expected? If yes, what is the workaround?
Update : Added classes description.
Given that your Element class is not a data class (in this case you would get a hashCode() method implementation based on all class properties) you will need to write the hashCode() method yourself so that the default one (based on object memory reference) is not used and you get rid of the behaviour you are currently facing.
I actually have a Class named "Content" like that :
public class Content {
private String Pame;
private String Tlue;
private String Ced;
private String Cnce;
#Override
public String toString() {
return "...";
}
}
On the other side I have a list of object of type "Content" with multiple objects in it.
My goal is to return a json formatted like this one:
There may be multiple values in the "content field", it's why there is [].
{
"result": "ok",
"content": [{
"pame": "value1",
"tlue": "value2",
"ced": "value3",
"cnce": "1"
}]
}
for(int i = 0; i < list.size(); i++){
String MyLongString = list.get(i).toString;
}
So my problem is :
How can I create the structure shown with the toString and the boucle For ?
How the "," at the end of each object can be put, without putting it at the last object ?
For information : I use Gson in my project
Thanks for helping
I'm trying to create a JSON document by using Jackson. The hierarchy goes as follows:
Event:
class Event {
private String name = "";
private Set<Integer> admin = new HashSet<>();
private List<House> houseList = new ArrayList<>();
}
House:
class House {
private List<OG> OGList = new ArrayList<>();
private int score = 0;
private String name = "";
}
Group:
class OG {
private int score = 0;
private int id = 0;
}
Every event might comprises of a set number of houses, which in turn comprises of a set number of groups. Each house and group has a score modifier as well.
Currently, this is how I print the JSON document using the pretty print method:
ObjectMapper mapper = new ObjectMapper();
File f = new File("./db/" + dir);
if (f.exists() && !f.isDirectory()) {
return "This event name is taken. Please try again.";
}
try {
mapper.writeValue(f, event);
// Convert object to JSON string and pretty print
String jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(event);
System.out.println(jsonInString);
}
}
The resulting output is pretty ugly:
{
"name" : "test",
"admin" : [ 423766405 ],
"houseList" : [ {
"score" : 0,
"name" : "first",
"oglist" : [ {
"score" : 0,
"id" : 0
}, {
"score" : 0,
"id" : 1
}, {
"score" : 0,
"id" : 2
} ]
..
}
Is there a better way to format the output, for example:
name:
test
admin:
a
b
c
houses:
name:
first
group:
1
..
It appears like you want to output YAML, not JSON.
This answer shows how simple it is to write YAML output to a file using Jackson.
This answer shows how to read a YAML file, modify it's contents, and save it back out again.
I have a JSON record that looks like this:
{"ActionRecord": {
"101": {
"Desc": "string 1",
"Done": 1,
"MaxTimes": 2,
"Point": 30,
"Times": 4
},
"102": {
"Desc": "string 2",
"Done": 1,
"MaxTimes": 3,
"Point": 15,
"Times": 13
},
"103": {
"Desc": "string 3.",
"Done": 1,
"MaxTimes": 5,
"Point": 15,
"Times": 24
}, ... }
I can get Jackson to parse this if i create a hacky intermediate class that contains a field for each number, and then use something like this in the class:
#JsonProperty( value = "101" )
public MyClass hundred_one;
#JsonProperty( value = "102" )
public MyClass hundred_two;
#JsonProperty( value = "103" )
public MyClass hundred_three;
But I have to type out all the expected values, so it would be much easier to be able to use an array list of objects, and insert the numeric id into the POJO with Jackson's mapper.
Is there a way have Jackson automatically map it into a class like this? :
public enum ActionRecord {
Something ( "101" ),
SomethingElse( "102" ),
AnotherSomething ( "103" ),
;
String _id;
EK_DailyTaskInfo_ActionRecord( String id )
{
_id = id;
}
public String getId()
{
return _id;
}
public String Desc; // "some string.",
public boolean Done; // 1,
public int Times; // 4
public int MaxTimes; // 2,
public int Point; // 30,
}
It does not have to be an enum this was just something I was trying before I gave up
Jackson can decode it into a Map<String, Record> for you, e.g.
public class Record {
public String Desc; // "some string.",
public boolean Done; // 1,
public int Times; // 4
public int MaxTimes; // 2,
public int Point; // 30,
}
public class ActionRecords {
public Map<String, Record> ActionRecord
}
well I am using GSON library in shown example, Android has it's own api to handle the JSON where a iterator is very useful, code is also available on github
What i want to propose is that you should read all the keys with respect to it's key's and get the JsonObject from there, you you have a JsonList which is not an array this is what you can do
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(result); // result is your json data
JsonObject obj = element.getAsJsonObject();
System.out.println(obj.toString());
JsonObject jsonObject = obj.getAsJsonObject("ActionRecord"); // this will get the JsonObject with the key ActionRecord
System.out.println(jsonObject);
Set<Map.Entry<String, JsonElement>> stringSet = jsonObject.entrySet(); // this will map all the JsonObject with it's keys
for (Map.Entry<String, JsonElement> key :stringSet) {
System.out.println(jsonObject.getAsJsonObject(key.getKey()).toString());
}
Once you have the key with it's corresponding JSONObject, you can create populate your own type for that object.
well this is for Gson, you might want to look for equivalent of this in Jackson
output
{"Desc":"string 1","Done":1,"MaxTimes":2,"Point":30,"Times":4}
{"Desc":"string 2","Done":1,"MaxTimes":3,"Point":15,"Times":13}
{"Desc":"string 3.","Done":1,"MaxTimes":5,"Point":15,"Times":24}
{
"Employee": [
{
"empMID": "mock:1",
"comments": [],
"col1": "something",
"contact": [{"address":"2400 waterview", "freetext":true}
],
"gender": "male"
},
{
"empMID": "mock:2",
"comments": [],
"col1": "something",
"contact": [{"address":"2200 waterview", "freetext":true}
],
"gender": "female"
}
],
"cola": false,
"colb": false
}
This is how my Json file looks .I m required to convert this json to a csv .(I m trying to convert a multi-dimesional data to 2d).I m using gson for my purpose.I cannot use gson.fromgson() function to object map with a template because it should be generic .
I know we can use CDL to convert jsonarray to csv format but It wont work in my case .
my csv format looks like
Employee*
empMID,comment.$,contact.address,contact.freetext,gender
mock:1,,2400 waterview,TRUE,male
mock:123,,2200 waterview,TRUE,female
colA#
TRUE
colB#
FALSE
I tried using google-GSON api to convert to this format .But I m not able to convert to this format .I have used * to represent its a json array and # to represent its a primitive type and contact.address to represent nested array inside another json array .I having problem relating this nested structure .I m able to traverse everything recursively like a column. Thanks in advance
public static void main(String[] args) throws IOException{
BufferedReader reader=null;
StringBuilder content=null;
String result=null;
reader = new BufferedReader(new FileReader("temp.json"));
String line = null;
content= new StringBuilder();
while ((line = reader.readLine()) != null) {
content.append(line);
}
reader.close();
result= content.toString();
JsonElement jelement = new JsonParser().parse(result);
printJsonRecursive(jelement);
}
public static void printJsonRecursive(JsonElement jelement){
if(jelement.isJsonPrimitive()){
System.out.println(jelement.getAsString());
return;
}
if(jelement.isJsonArray()){
JsonArray jarray= jelement.getAsJsonArray();
for(int i=0;i<jarray.size();i++){
JsonElement element= jarray.get(i);
printJsonRecursive(element);
}
return;
}
JsonObject jobject= jelement.getAsJsonObject();
Set<Entry<String, JsonElement>> set= jobject.entrySet();
for (Entry<String, JsonElement> s : set) {
printJsonRecursive(s.getValue());
}
}
}
You can achieve this thru reflection if you have a object mapped to the json.
use gson/jackson to convert json to java object
append fields using reflection by iterating the class and get any field you interested in.
append value with reflection by getting value from the target object.
More detail look at my blog post below:
vcfvct.wordpress.com/2015/06/30/converting-nested-json-files-to-csv-in-java-with-reflection/
You are not printing the key. This should fix it.
for (Entry<String, JsonElement> s : set) {
System.out.println(s.getKey()); //Added
printJsonRecursive(s.getValue());
}
You can take care of \ns from here.
EDIT
If you want to print the keys just once for repeating json objects, create a Java bean to hold the data and populate it during your recursion. Once the bean is complete, add a method there to print all the data in the format you want (printing keys only once and so on).
You can use the library json2flat for converting your JSON to CSV.
This library doesn't require any POJO's. It simply takes your JSON as string and returns a 2D representation of it in the format of List<Object[]>.
For example for the JSON:
{
"Employee": [
{
"empMID": "mock:1",
"comments": [],
"col1": "something",
"contact": [{"address":"2400 waterview", "freetext":true}
],
"gender": "male"
},
{
"empMID": "mock:2",
"comments": [],
"col1": "something",
"contact": [{"address":"2200 waterview", "freetext":true}
],
"gender": "female"
}
],
"cola": false,
"colb": false
}
It gives an output:
/cola,/colb,/Employee/empMID,/Employee/col1,/Employee/gender,/Employee/contact/address,/Employee/contact/freetext
,,"mock:1","something",,"2400 waterview",true
,,"mock:2","something",,"2200 waterview",true
false,false,,,,,
/**
* Get separated comlumns used a separator (comma, semi column, tab).
*
* #param headers The CSV headers
* #param map Map of key-value pairs contains the header and the value
*
* #return a string composed of columns separated by a specific separator.
*/
private static String getSeperatedColumns(Set<String> headers, Map<String, String> map, String separator) {
List<String> items = new ArrayList<String>();
for (String header : headers) {
String value = map.get(header) == null ? "" : map.get(header).replaceAll("[\\,\\;\\r\\n\\t\\s]+", " ");
items.add(value);
}
return StringUtils.join(items.toArray(), separator);
}