gson model for json array without key - json

I have the following json from our customer:
{
"id": 1234,
"delivery_date": 1234567890,
"actions": [
[ "foo", true],
[ "bar", true]
],
"customer": {
"id": 12345,
"company": "",
"firstname": "John",
"lastname": "Smith",
"action": ["dothis", true]
},
"childs": [ 123abc2132312312,11232432943493]
}
I want to parse the "actions" array as a List< Actions> actionList and the single "action" as Action action.
With
class Action {
String action;
boolean yesno;
}
And the childs Array as List< Child> childs with
class Child{
String id
}
Is that possible without the json keys?

Edit:
Your action class is ok, i mis-read slightly.
Add a complete class:
class Delivery {
Int32 id;
Int32 delivery_date;
list<Action> actions;
Customer customer;
list<Int32> childs;
}
actions will be parsed as a paramtere inside, as will childs.
Then you need to create a Customers class too, which is part of this. (or exclude it, and GSON will ignore it)
This will populate the ints into childs and Actions into actions.
If indeed, childs is alphanumeric, then just change it to String.
You can then access it via,
Delivery delivery = GSON ... etc
var x = delivery.actions; // Actions
var y = delivery.childs; // Childs

I solved it my self with a custom deserializer. Thanks to dzsonni for the hint.
In the Gson root class:
private ArrayList<Action> parcel_actions = new ArrayList<Action>();
the action class
class Action {
String action;
boolean yesno;
}
the deserializer:
public class ActionDeserializer implements JsonDeserializer<ArrayList<Action>> {
#Override
public ArrayList<Action> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
ArrayList<Actions> list = new ArrayList<Action>(){};
if(json.getAsJsonArray().get(0).isJsonPrimitive()){
String action = json.getAsJsonArray().get(0).getAsString();
boolean doIt = json.getAsJsonArray().get(1).getAsBoolean();
list.add(new Action(action, doIt));
}
else {
for(JsonElement element : json.getAsJsonArray()) {
String action = element.getAsJsonArray().get(0).getAsString();
boolean doIt = element.getAsJsonArray().get(1).getAsBoolean();
list.add(new Action(action, doIt));
}
}
return list;
}
}
then just add it to your gson
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(new TypeToken<ArrayList<Action>>(){}.getType(), new ActionsDeserializer());
Gson gson = builder.create();

Related

Jackson Polymorphic Deserialization and serialize

I will two different types of in separate calls. I want to deserialize or serialize depending on the business requirement.
one type of json:
{
"type": "foo",
"data": [{
"someCommonProperty": "common property",
"fooProperty": "foo specific property"
},{
"someCommonProperty": "common property1",
"fooProperty": "foo specific property1"
}]
}
Another type of json:
{
"type": "bar",
"data": [{
"someCommonProperty": "common property",
"barProperty": "bar specific property",
"type": "type1"
},{
"someCommonProperty": "common property1",
"barProperty": "bar specific property1",
"type": "type1"
}]
}
I have classes as below
public class Parent {
private String type;
#JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY)
#JsonSubTypes(value = {
#JsonSubTypes.Type(value = Foo.class, name = "foo"),
#JsonSubTypes.Type(value = Bar.class, name = "bar")
})
private List<AbstractData> data;
// Getters and setters
}
public abstract class AbstractData {
private String someCommonProperty;
// Getters and setters
}
public class Foo extends AbstractData {
private String fooProperty;
// Getters and setters
}
public class Bar extends AbstractData {
private String barProperty;
private String type;
// Getters and setters
}
When I try to deserialize as below, I am getting empty string on writing out java object as json.
ObjectMapper mapper = new ObjectMapper();
Parent parent = mapper.readValue(json, Parent.class);
I get this error:
com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class com.DemoJsonToJavaDeserialize.AbstractData
at [Source: C:<projectpath>\target\classes\foo.json; line: 3, column: 12] (through reference chain: com.DemoJsonToJavaDeserialize.Parent["data"]->java.util.ArrayList[0])
Option 1
If you have access to the AbstractData class, you might want to use the new 2.12 Jackson Deduction feature :
#JsonSubTypes(value = {
#JsonSubTypes.Type(value = Foo.class),
#JsonSubTypes.Type(value = Bar.class)
})
#JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
public abstract class AbstractData {
private String someCommonProperty;
// getters setters
}
Then, no need to annotate Foo, Bar and Parent : everything will get instantiated automagically as long as your subclasses have different property names. type json property becomes useless as well.
(If you are using SpringBoot, you can set the jackson-bom.version property to upgrade jackson inside spring boot)
Option 2
the javadoc of JsonTypeInfo.As.EXTERNAL_PROPERTY says : Note also that this mechanism can not be used for container values (arrays, Collections... . I believe this is your problem: you're applying it to a List.
If you can't modify any of the Foo/Barr/AbstractData classes, the classic way is to use a custom Deserializer.
On the Parent class :
#JsonDeserialize(contentUsing = FooBarDeserializer.class)
private List<AbstractData> data;
And the Deserializer:
public class FooBarDeserializer extends JsonDeserializer<AbstractData> {
#Override
public AbstractData deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
JsonNode node = jsonParser.readValueAsTree();
if(node.has("fooProperty"))
return jsonParser.getCodec().treeToValue(node,Foo.class);
else
return jsonParser.getCodec().treeToValue(node,Bar.class);
}
}
If you find a solution that make use of your Parent.type , please share, I'm interested.

Android Kotlin parsing nested JSON

I'm still pretty new to programming with Kotlin but I can't seem to figure out the correct way to parse my JSON. I'm attempting to get "title" and "body" from "notification" in "unackd" array only.
So far I've got:
private fun parse(): Boolean {
try {
val ja = JSONArray(jsonData)
var jo: JSONObject
users.clear()
var user: User
for (i in 0 until ja.length()) {
jo = ja.getJSONObject(i)
val name = jo.getString("title")
val username = jo.getString("body")
user = User(username,name)
users.add(user)
}
return true
} catch (e: JSONException) {
e.printStackTrace()
return false
}
}
Meanwhile my JSON is structured as so:
{
"unackd": [
{
"notification": {
"title": "Title Test Number 200",
"body": "passage local they water difficulty tank industry allow increase itself captured strike immediately type phrase driver change save potatoes stems addition behavior grain trap rapidly love refused way television bright 1100"
},
"data": {
"id": "1100",
"phone": "+15555551234"
}
},
{
"notification": {
"title": "Title Test Number 199",
"body": "announced beside well noted mysterious farm he essential likely deeply vast touch 1099"
},
"data": {
"id": "1099",
"phone": "+15555551234"
}
}
],
"ackd": [
{
"notification": {
"title": "Title Test Number 200",
"body": "passage local they water difficulty tank industry allow increase itself captured strike immediately type phrase driver change save potatoes stems addition behavior grain trap rapidly love refused way television bright 1100"
},
"data": {
"id": "1100",
"phone": "+15555551234"
}
},
{
"notification": {
"title": "Title Test Number 199",
"body": "announced beside well noted mysterious farm he essential likely deeply vast touch 1099"
},
"data": {
"id": "1099",
"phone": "+15555551234"
}
}
]
}
I believe my issue is getting into "notification" to then get the strings "title" and "body". Which I've tried
test1 = jo.getJSONObject("notification")
Any help would be appreciated!
EDIT:
This is my logcat error, I assume it has to do with the JSON.typeMismatch:
at org.json.JSON.typeMismatch(JSON.java:111)
at org.json.JSONArray.<init>(JSONArray.java:96)
at org.json.JSONArray.<init>(JSONArray.java:108)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
The exception message suggests that you're passing data that doesn't represent a JSON array when instantiating JSONArray:
at org.json.JSON.typeMismatch(JSON.java:111)
at org.json.JSONArray.<init>(JSONArray.java:96)
The JSON you've attached is in fact a JSON object, notice that its content is enclosed in {}. Hence to access the "unackd" array, you need to first create a JSON object, and then reference the array inside of it:
val root = JSONObject(jsonData)
val ja = root.getJSONArray("unackd")
// the rest of your code goes here
Listen friend , parsing the JSON Object with JSON ARRAY with key (like: unackd , ackd) is so simple.
There are 2 ways:
1st Way)
Parse your JSON to Pojo schema
http://www.jsonschema2pojo.org/
public class Ackd {
#SerializedName("notification")
#Expose
private Notification_ notification;
#SerializedName("data")
#Expose
private Data_ data;
public Notification_ getNotification() {
return notification;
}
public void setNotification(Notification_ notification) {
this.notification = notification;
}
public Data_ getData() {
return data;
}
public void setData(Data_ data) {
this.data = data;
}
}
public class Data {
#SerializedName("id")
#Expose
private String id;
#SerializedName("phone")
#Expose
private String phone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
No need to Make all class for parsing (like ackd (Json Array))
2nd Way)
You need to PARSE JSON array with name only unackd not ackd.
String jsonStr = sh.makeServiceCall(url);
JSONObject jsonObj = new JSONObject(jsonStr);
// Getting JSON Array node
JSONArray unA= jsonObj.getJSONArray("unackd");
for (int i = 0; i < unA.length(); i++)
{
JSONObject c = unA.getJSONObject(i);
String title= c.getString("title");
String body= c.getString("body");
}
Auto generate Data class
http://www.jsonschema2pojo.org/
I suppose that your class is named Response.java
Response object=new Gson().fromjson(jsonContentFile,Response.class);
Following data classes are generated for your JSON using https://json2kotlin.com
data class Json4Kotlin_Base (
val unackd : List<Unackd>,
val ackd : List<Ackd>
)
and
data class Data (
val id : Int,
val phone : Int
)
and
data class Notification (
val title : String,
val body : String
)
and
data class Ackd (
val notification : Notification,
val data : Data
)
and
data class Unackd (
val notification : Notification,
val data : Data
)
Here's a video that explains how to implement these when generated.

Map<String, HashSet<String>> to JSON, & Pretty Print

I'm trying to make my dataset correspond to this example:
var family = [{
"name" : "Jason",
"age" : "24",
"gender" : "male"
},
{
"name" : "Kyle",
"age" : "21",
"gender" : "male"
}];
I have a Map<String, HashSet<String>> of Names and unique alpha-numeric values correponding to specific entities to which those names could refer, let's call these entry items "IDs".
So for instance, Fyodor Mikhailovich Dostoyevsky would perhaps be related to the ID Q626, because that's a very specific reference, there aren't many widely known figures with that name. Whereas, Bush might be attached to G027, Q290, and Q118, referencing perhaps the man, the beer, and the shrub, in no particular order.
It looks like this (the real one is much bigger):
[Rao=[Q7293658, , Q7293657, Q12953055, Q3531237, Q4178159, Q1138810, Q579515, Q3365064, Q7293664, Q1133815], Hani Durzy=[], Louise=[, Q1660645, Q130413, Q3215140, Q152779, Q233203, Q7871343, Q232402, Q82547, Q286488, Q156723, Q3263649, Q456386, Q233192, Q14714149, Q12125864, Q57669, Q168667, Q141410, Q166028], Reyna=[Q7573462, Q2892895, Q363257, Q151944, Q3740321, Q2857439, Q1453358, Q7319529, Q733716, Q16151941, Q7159448, Q5484172, Q6074271, Q1753185, Q7319532, Q5171205, Q3183869, Q1818527, Q251862, Q3840414, Q5271282, Q5606181]]
Using Jackson I tried like this:
Map<String, HashSet<String>> map = q_valMap;
mapper.writeValue(new File("JSON_Output/user.json"), map);
But this seems wrong, as my output was all jumbled together, i.e.
{"Rao":["Q7293658","","Q7293657","Q12953055","Q3531237","Q4178159","Q1138810","Q579515","Q3365064","Q7293664","Q1133815"],"Hani Durzy":[""],"Louise":["","Q1660645","Q130413","Q3215140","Q152779","Q233203","Q7871343","Q232402","Q82547","Q286488","Q156723","Q3263649","Q456386","Q233192","Q14714149","Q12125864","Q57669","Q168667","Q141410","Q166028"],"Reyna":["Q7573462","Q2892895","Q363257","Q151944","Q3740321","Q2857439","Q1453358","Q7319529","Q733716","Q16151941","Q7159448","Q5484172","Q6074271","Q1753185","Q7319532","Q5171205","Q3183869","Q1818527","Q251862","Q3840414","Q5271282","Q5606181"]}
Do I just have to populate this JSON object iteratively?
Like the example up top, I think it should look something like this, though what follows is only a pseudocodish characterization, which is to say, not exactly this but something similar:
{
key: "Rao"
value: ["Q7293658","","Q7293657","Q12953055","Q3531237","Q4178159","Q1138810","Q579515","Q3365064","Q7293664","Q1133815"]
key: "Hani Durzy"
value: [""]
key: "Louise"
value: ["","Q1660645","Q130413","Q3215140","Q152779","Q233203","Q7871343","Q232402","Q82547","Q286488","Q156723","Q3263649","Q456386","Q233192","Q14714149","Q12125864","Q57669","Q168667","Q141410","Q166028"]
key: "Reyna"
value: ["Q7573462","Q2892895","Q363257","Q151944","Q3740321","Q2857439","Q1453358","Q7319529","Q733716","Q16151941","Q7159448","Q5484172","Q6074271","Q1753185","Q7319532","Q5171205","Q3183869","Q1818527","Q251862","Q3840414","Q5271282","Q5606181"]
}
is that not right?
UPDATE
public class JsonMapFileExample
{
public static void map(Map<String, HashSet<String>> q_valMap )
{
ObjectMapper mapper = new ObjectMapper();
ArrayNode array = mapper.createArrayNode();
for ( Entry entry: q_valMap.entrySet() )
{
ObjectNode node = mapper.createObjectNode()
.put("name", entry.getKey())
.put("ids", entry.getValue());
array.add(node);
}
mapper.writeValue("/home/matthias/Workbench/SUTD/nytimes_corpus/wdtk-parent/wdtk-examples/JSON_Output/user.json", array);
}
}
class MyEntity
{
private String name;
Set<String> value; // use names that you want in the result JSON
//constructors
public MyEntity()
{
}
public MyEntity(String name)
{
this.name = name;
}
//getters
public String getName()
{
return this.name;
}
public Set<String> getValue()
{
return this.value;
}
//setters
public void setName(String name)
{
this.name = name;
}
public void setValue(Set<String> value)
{
this.value = value;
}
}
You could manually set the key names, something like:
ArrayNode array = mapper.createArrayNode();
for (Entry entry: yourMap.entries()) {
ObjectNode node = mapper.createObjectNode()
.put("name", entry.key())
.putPOJO("ids", entry.value());
array.add(node);
}
mapper.writeValue(file, array);
Alternatively, you could create a class for your data
class MyEntity {
String name;
Set<String> ids; // use names that you want in the JSON result
// getters, setters if necessary
}
Transform your data map into a list of MyEntity, then use Jackson ObjectMapper to create JSON like mapper.writeValue(file, listOfMyEntities), the output would be like
[
{
"name": "some name here",
"ids": ["id1", "id2", ...]
}
// more elements here
]
how about this:
String name_list_file = "/home/matthias/Workbench/SUTD/nytimes_corpus/NYTimesCorpus/2005/01/02/test/people_test.txt";
String single_name;
try (
// read in the original file, list of names, w/e
InputStream stream_for_name_list_file = new FileInputStream( name_list_file );
InputStreamReader stream_reader = new InputStreamReader( stream_for_name_list_file , Charset.forName("UTF-8"));
BufferedReader line_reader = new BufferedReader( stream_reader );
)
{
while (( single_name = line_reader.readLine() ) != null)
{
//replace this by a URL encoder
//String associated_alias = single_name.replace(' ', '+');
String associated_alias = URLEncoder.encode( single_name , "UTF-8");
String platonic_key = single_name;
System.out.println("now processing: " + platonic_key);
Wikidata_Q_Reader.getQ( platonic_key, associated_alias );
}
}
//print the struc
Wikidata_Q_Reader.print_data();
}

How to create proper JAXB mapping to make Jersey deserialization process happened

I have JSON response from WS:
[
{
"name": "Bobby",
"status": "single"
},
{
"name": "John",
"status": "married"
}
]
Here is my wrapper
#XmlRootElement(name = "users")
public class UserListWrapper {
private List<User> users;
#XmlElement(name = "user")
public List<User> getUsers() {
return users;
}
// getters and setters omitted
}
And User class
#XmlRootElement
class User {
private String name;
private String status;
// getters and setters omitted
}
The problem is when Jersey try to deserialize response to my wrapper object. It say
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of com.jersey.test.UserListWrapper out of START_ARRAY token
Seams that something wrong with my wrapper annotations. How can I fix them?
UPD
When I send
{
"user": [
{
"name": "Bob",
"status": "single"
},
{
"name": "Mike",
"status": "married"
}
]
}
all works fine. But I need this format
[
{
"name": "Bobby",
"status": "single"
},
...
]
UPD
Jersey Client code
HttpAuthenticationFeature authenticationFeature = HttpAuthenticationFeature.basic("user", "secret");
Client client = ClientBuilder
.newClient()
.register(authenticationFeature)
.register(JacksonFeature.class);
WebTarget target = client.target("http://localhost:8080/server/");
UserListWrapper entity;
Response resp;
resp = target.queryParam("u", "info")
.path("/rest/users")
.request()
.accept(APPLICATION_JSON)
.get();
entity = resp.readEntity(UserListWrapper.class);
Forget the UserListWrapper wrapper then. List<User> is perfect for the JSON array ( [] ) format. If you add the wrapper class, then yes you will need the extra JSON object layer ( {} ). This:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response createBook(List<User> users) {
is supported just fine (at least with Jackson - which you are using).
UPDATE
If the response from the server is coming as a JSON array, then you can still deserialize it as a List<User>. For example
WebResource resource = client.resource("...");
List<User> users = resource.get(new GenericType<List<User>>(){});
See this related post
UPDATE 2
Since you are using the JAX-RS 2 client API, you can use the overloaded readEntity, which accepts a GenericType argument also
List<User> user = response.readEntity(new GenericType<List<User>>(){});

Customize serialization of a list of JAXB objects to JSON?

I am planning to serialize list of JAXB objects to JSON response. Currently below is the format I am getting. In the below response I am seeing one more object in between is "systemInfoList" which actually is showing the array. Instead I want the dependent_systems_infos should directly show array []. Also if there is a single system info response also still it should should show in the array format. I am using the Jackson parser, cxf.
Format currently I am getting:
{
"dependent_systems_infos":{
"systemInfoList":[
{
"system_name":"PZ_Service",
"system_type":"Internal",
"service_infos":[
{
"service_name":"getPZAttributes",
"status":"DOWN",
"response_time_ms":50
}
]
},
{
"system_name":"OMS",
"system_type":"External",
"service_infos":[
{
"service_name":"CreateOrder",
"status":"UP",
"response_time_ms":2000
},
{
"service_name":"CancelOrder",
"status":"UP",
"response_time_ms":500
}
]
}
]
}
}
Format I need:
{
dependent_system_infos : [
{
system_name : 'OMS'
system_type: 'External'
services_infos: [
{
service_name : 'CreateOrder'
status : 'UP'
response_time_ms : 2000
},
{
service_name : 'CancelOrder'
status : 'UP'
response_time_ms : 2000
}
]
},
{
system_name : 'PZ_Service'
system_type: 'Internal'
services_infos: [
{
service_name : 'getPZAttributes'
status : 'UP'
response_time_ms : 2000
}
]
}
]
}
JAXB classes I wrote:
#XmlRootElement(name = "dependent_systems_infos")
#XmlAccessorType(XmlAccessType.FIELD)
public class ItineraryStatusResponse {
private List<SystemInfo> systemInfoList;
#XmlList
public List<SystemInfo> getSystemInfoList() {
return systemInfoList;
}
public void setSystemInfoList(List<SystemInfo> systemInfoList) {
this.systemInfoList = systemInfoList;
}
}
#XmlType(propOrder = {
"systemName",
"systemType",
"serviceInfoList"
})
#XmlAccessorType(XmlAccessType.FIELD)
public class SystemInfo {
#XmlElement(name = "system_name", required = true)
protected SystemName systemName;
#XmlElement(name = "system_type", required = true)
protected SystemType systemType;
#XmlElement(name = "service_infos", required = true)
protected List<ServiceInfo> serviceInfoList;
}
It would help to know how you're generating the output, but the main issue is that you are serializing a root object that contains a list when you really only want to serialize the list itself. What would you expect the outputted list to look like if ItineraryStatusResponse had other fields in it?
You can remove the #XmlRootElement annotation and mark the list as an element named "dependent_systems_infos":
#XmlAccessorType(XmlAccessType.FIELD)
public static class ItineraryStatusResponse {
private List<SystemInfo> systemInfoList;
#XmlElement(name = "dependent_systems_infos", required = true)
public List<SystemInfo> getSystemInfoList() {
return systemInfoList;
}
public void setSystemInfoList(List<SystemInfo> systemInfoList) {
this.systemInfoList = systemInfoList;
}
}
If you are doing the serialization yourself, another approach would be to drop the ItineraryStatusResponse object entirely (since it's just a wrapper around the list), and then serialize the list itself with SerializationFeature.WRAP_ROOT_VALUE = true and a root name you provide:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
mapper.setAnnotationIntrospector(introspector);
ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter().withRootName("dependent_systems_infos");
System.out.println(writer.writeValueAsString(systemInfoList));
Both of these approaches provide the desired output in my testing with Jackson 2.2.