deserialize a json array using gson - java

I'm deserializing a json object like this:
class Offer
{
private Category category;
private String description;
private String discount;
private Date expiration;
private Date published;
private String rescinded_at;
private String title;
private Date valid_from;
private Date valid_to;
private String id;
private Business business;
private Location location;
private Long distance;
public String getDescription() {
return String.format("[Offer: description=%2$s]", description);
}
#Override
public String toString()
{
return String.format(
"[Offer: category=%1$s, description=%2$s, discount=%3$s, expiration=%4$s, published=%5$s, rescinded_at=%6$s, title=%7$s, valid_from=%8$s, valid_to=%9$s, id=%10$s, business=%11$s, location=%12$s, distance=%13$s]",
category, description, discount, expiration, published, rescinded_at, title, valid_from, valid_to, id,
business, location, distance);
}
}
As you can see, whenever there's a nested object I just refer to a class that has a toString() method for that particular nested json object. My question is: when the json object contains an array, which in my case just looks something like this:
"draws":[
"Hair Cut",
"Blow Dry",
"Blow Dry Treatment"
]
...how do I use format.toString() to deserialize this array and then put it in my Offer toString()?

Let's clarify the meaning of two terms.
Serialize: To convert an object to a sequence of bytes.
Deserialize: To parse (serialized data) so as to reconstruct the original object.
So #LuxuryMode, when you said "deserialize", did you mean "serialize"?
Assuming this is the case...
Note that your toString implementation does not currently properly generate a JSON object or array, or anything else that is valid JSON.
I recommend not using toString or any other hand-written implementation to serialize objects to JSON (or to XML or to bytes). If possible, use an API like Gson or Jackson (or XStream or the Java Serialization API).
The following example serializes a single Offer object.
// output:
// {
// "category":
// {
// "name":"category_1",
// "type":1
// },
// "description":"description_1",
// "discount":"discount_1",
// "expiration":
// {
// "value":123
// },
// "published":
// {
// "value":456
// },
// "rescinded_at":"rescinded_at_1",
// "title":"title_1",
// "valid_from":
// {
// "value":789
// },
// "valid_to":
// {
// "value":987
// },
// "id":"id_1",
// "business":
// {
// "name":"business_name_1",
// "opening_date":
// {
// "value":654
// }
// },
// "location":
// {
// "latitude":111,
// "longitude":222
// },
// "distance":333
//}
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class Foo
{
public static void main(String[] args)
{
Offer offer = new Offer(
new Category("category_1", 1),
"description_1",
"discount_1",
new Date(123),
new Date(456),
"rescinded_at_1",
"title_1",
new Date(789),
new Date(987),
"id_1",
new Business("business_name_1", new Date(654)),
new Location(111, 222),
new Long(333));
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
Gson gson = gsonBuilder.create();
String offerJson = gson.toJson(offer);
System.out.println(offerJson);
}
}
class Offer
{
private Category category;
private String description;
private String discount;
private Date expiration;
private Date published;
private String rescindedAt;
private String title;
private Date validFrom;
private Date validTo;
private String id;
private Business business;
private Location location;
private Long distance;
Offer(Category category,
String description,
String discount,
Date expiration,
Date published,
String rescindedAt,
String title,
Date validFrom,
Date validTo,
String id,
Business business,
Location location,
Long distance)
{
this.category = category;
this.description = description;
this.discount = discount;
this.expiration = expiration;
this.published = published;
this.rescindedAt = rescindedAt;
this.title = title;
this.validFrom = validFrom;
this.validTo = validTo;
this.id = id;
this.business = business;
this.location = location;
this.distance = distance;
}
}
class Category
{
private String name;
private int type;
Category(String name, int type)
{
this.name = name;
this.type = type;
}
}
class Date
{
private long value;
Date(long value)
{
this.value = value;
}
}
class Business
{
private String name;
private Date openingDate;
Business(String name, Date openingDate)
{
this.name = name;
this.openingDate = openingDate;
}
}
class Location
{
private int latitude;
private int longitude;
Location(int latitude, int longitude)
{
this.latitude = latitude;
this.longitude = longitude;
}
}
This next example takes the JSON output from the previous example, and deserializes it back into a Java Offer object. You can add toString and/or equals implementations to verify that all of the attributes are populated as expected, but note that the toString method is not used by Gson during deserialization or serialization.
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
Gson gson = gsonBuilder.create();
String offerJson = gson.toJson(offer);
Offer offerDeserialized = gson.fromJson(offerJson, Offer.class);
To serialize an array of Offer objects is similarly simple.
Offer offer1 = new Offer(
new Category("category_1", 1),
"description_1",
"discount_1",
new Date(123),
new Date(456),
"rescinded_at_1",
"title_1",
new Date(789),
new Date(987),
"id_1",
new Business("business_name_1", new Date(654)),
new Location(111, 222),
new Long(333));
Offer offer2 = new Offer(
new Category("category_2", 2),
"description_2",
"discount_2",
new Date(234),
new Date(567),
"rescinded_at_2",
"title_2",
new Date(890),
new Date(876),
"id_2",
new Business("business_name_2", new Date(543)),
new Location(444, 555),
new Long(666));
Offer[] offers = new Offer[] {offer1, offer2};
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
Gson gson = gsonBuilder.create();
String offersJson = gson.toJson(offers);
System.out.println(offersJson);
This final example takes the JSON array output from the previous example and deserializes it back into an array of Offer objects.
Offer[] offersDeserialized = gson.fromJson(offersJson, Offer[].class);

Related

gson deserializing a field as null

First of all, I'd like to say this is for a university project.
I have 3 classes. Order abstract class and Delivery and DineIn classes which inherits from Order.
I am using Gson to serialize/deserialize the child classes but I have run into a bit of a problem. The Order class has a field orderType which gson uses to determine type of order it is, DineIn or Delivery.
Serialization is working just fine. The problem is that whenever I try to deserialize, the type field value is not read and is always set as null even though it is present in the JSON file.
This happens when there are a lot of fields in Order because when I tried testing this program on a smaller scale with the Order class just having 2 fields (orderType and orderNo) everything worked just fine. I don't what I am doing wrong. I have tried searching on this site and am almost always coming across suggestions to make custom type adapters and serializers but we haven't studied about them in university and I don't want to use them (the instructor deducts marks for using anything he hasn't taught, I almost failed a course I took from him last time because I used things he hadn't taught. He doesn't seem to have a problem with third-party libraries though).
The code:
public class Main {
public static final List<Order> ordersList = read();
public static void main(String[] args) {
System.out.println(ordersList.get(0).getOrderType());
System.out.println(ordersList.get(0) instanceof DineIn ? "DineIn": "Delivery");
}
private static List<Order> read(){
List<Order> ordersList = new ArrayList<>();
Type type = new TypeToken<ArrayList<Order>>() {
}.getType();
RuntimeTypeAdapterFactory<Order> adapter = RuntimeTypeAdapterFactory.of(Order.class, "orderType")
.registerSubtype(DineIn.class)
.registerSubtype(Delivery.class);
Gson gson = new GsonBuilder().registerTypeAdapterFactory(adapter).create();
JsonReader ordersJsonReader;
try {
ordersJsonReader = new JsonReader(new FileReader("orders.json"));
List<Order> tempOrdersList = gson.fromJson(ordersJsonReader, type);
if (tempOrdersList != null) ordersList = tempOrdersList;
ordersJsonReader.close();
} catch (IOException e) {
e.printStackTrace();
}
return ordersList;
}
}
abstract class Order {
private final int orderNumber;
private final String date, customerName;
private final int discountRate;
private final String paymentMethod;
private String orderStatus;
private int grossTotal = 0;
private double netTotal = 0;
private int totalItems = 0;
protected final String orderType;
public abstract String getOrderType();
public abstract double getExtraCharges();
public Order(int orderNumber, String date, String customerName, int discountRate, String paymentMethod, String orderStatus, int grossTotal, double netTotal, int totalItems, String orderType) {
this.orderNumber = orderNumber;
this.date = date;
this.customerName = customerName;
this.discountRate = discountRate;
this.paymentMethod = paymentMethod;
this.orderStatus = orderStatus;
this.grossTotal = grossTotal;
this.netTotal = netTotal;
this.totalItems = totalItems;
this.orderType = orderType;
}
}
class DineIn extends Order {
private double serviceCharges = 150;
public DineIn(int orderNumber, String date, String customerName, int discountRate, String paymentMethod, String orderStatus, int grossTotal, double netTotal, int totalItems) {
super(orderNumber, date, customerName, discountRate, paymentMethod, orderStatus, grossTotal, netTotal, totalItems, "DineIn");
}
#Override
public String getOrderType() {
return orderType;
}
#Override
public double getExtraCharges() {
return serviceCharges;
}
}
class Delivery extends Order {
private double deliveryCharges = 100;
public Delivery(int orderNumber, String date, String customerName, int discountRate, String paymentMethod, String orderStatus, int grossTotal, double netTotal, int totalItems) {
super(orderNumber, date, customerName, discountRate, paymentMethod, orderStatus, grossTotal, netTotal, totalItems, "Delivery");
}
#Override
public String getOrderType() {
return orderType;
}
#Override
public double getExtraCharges() {
return deliveryCharges;
}
}
The JSON:
[
{
"serviceCharges": 150.0,
"orderNumber": 1,
"date": "12/12/2021",
"customerName": "Ali",
"discountRate": 15,
"paymentMethod": "Card",
"orderStatus": "Preparing",
"grossTotal": 5000,
"netTotal": 4500.0,
"totalItems": 14,
"orderType": "DineIn"
}
]
In your code you have a hierarchy where DineIn and Delivery extend from Order. The way the orderType field is set is through an explicit String argument in the super() constructor.
However, Gson does not use the constructor to instantiate the objects. It uses a special no-argument constructor and populates the values via reflection: https://stackoverflow.com/a/40442037/9698467
In this specific case the problem comes from the RuntimeTypeAdapterFactory, which removes the orderType field from the JSON that it reads. The source code here confirms that: https://github.com/google/gson/blob/86d88c32cf6a6b7a6e0bbc855d76e4ccf6f120bb/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java#L202
As #fluffy suggested newer versions of the library include the maintain flag, which should allow for the field to be preserved: https://github.com/google/gson/blob/c1e7e2d2808b042cbe47ca31869ee6ccc62c5417/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java#L214

JSON to GSON and POJO class

I have a JSON response like the following:
{
"number":"123456789101112",
"dimensions":"{\"height\":\"8.200\",\"width\":\"18.800\",\"depth\":\"9.400\"}",
"uom":"centimetre"
}
I am using the following to convert from JSON to GSON
Gson gson = new Gson();
Details details = gson.fromJson(JSONresponse, Details .class);
I want to know how to map the JSON response into my pojo class for converting this JSON to GSON and retrieving individual values.
I want to know how to access the height,width,depth from the response. This is my current Details class:
public class Details
{
private String number;
private String dimensions;
private String uom;
public void setNumber(String number){
this.number = number;
}
public String getNumber(){
return this.number;
}
public void setDimensions(String dimensions){
this.dimensions = dimensions;
}
public String getDimensions(){
return this.dimensions;
}
public void setUom(String uom){
this.uom = uom;
}
public String getUom(){
return this.uom;
}
}
I would model your POJO as:
public class Details {
private Integer number;
private Dimension dimensions; // lowercase according to Java naming conventions
private String uom;
// getters and setters
}
public class Dimension {
private String height;
private String width;
private String depth;
// getters and setters
}
Then just use your current (correct) code, and then access whichever fields you want:
Gson gson = new Gson();
Details details = gson.fromJson(JSONresponse, Details.class);
String height = details.getDimensions().getHeight();

mergin different ArrayList types to ArrayList Object

I download a JSON file with following format:
{
"name": "enter_username",
"longitude": "22.601952",
"latitude": "40.674065",
"type": "Big_Pothole"
}
(this is the actual file http://zachtsou.webpages.auth.gr/FetchLocationData.php).
And I store them in different ArrayLists.
For example :
private void getDBLocations(JSONArray j){
dblocations = new ArrayList<LatLng>();
name = new ArrayList<>();
types = new ArrayList<String>();
full= new ArrayList<>();
// Traversing through all the items in the Json array
for(int i=0;i<j.length();i++){
try{
//Getting json object
JSONObject json = j.getJSONObject(i);
// Adding types to array list
names.add(json.getString(Config.TAG_NAME));
type.add(json.getString(Config.TAG_TYPE));
longitude.add(json.getDouble(Config.TAG_LONGITUDE));
latitude.add(json.getDouble(Config.TAG_LATITUDE));
}catch (JSONException e){
e.printStackTrace();
}
// Combining Longitude and Latitude on ArrayList<LatLon>
dblocations.add(new LatLng(latitude.get(i), longitude.get(i)));
}
}
I would like to make one common ArrayList so to be able to transfer it to new activity(for example google maps activity) and print all the items with a for loop.
You can create one class for Location data like below..
public class LocationData {
private String name;
private String longitude;
private String latitude;
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLongitude() {
return longitude;
}
public void setLongitude(String longitude) {
this.longitude = longitude;
}
public String getLatitude() {
return latitude;
}
public void setLatitude(String latitude) {
this.latitude = latitude;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
After that, parse your JSONArray response as below
private void getDBLocations(JSONArray j){
ArrayList<LocationData> locations = new ArrayList<LocationData>();
// Traversing through all the items in the Json array
for(int i=0;i<j.length();i++){
try{
//Getting json object
JSONObject json = j.getJSONObject(i);
LocationData location = new LocationData();
location.setName(json.getString(Config.TAG_NAME));
location.setType(json.getString(Config.TAG_TYPE));
location.setLongitude(json.getDouble(Config.TAG_LONGITUDE));
location.setLatitude(json.getDouble(Config.TAG_LATITUDE));
locations.add(location);
}catch (JSONException e){
e.printStackTrace();
}
}
}
Now you have one common ArrayList of locations. You can use it as you want.
Quick answer - use a json library to parse a JSON String - example bellow.
You can have one Object - Activity. Add getters and setters.
public class Activity {
private String name;
private String type;
private LngLtd location;
}
public class LngLtd {
private double latitude;
private double longitude;
}
Then you need convert JSON String to Java Object. You can use different libraries. This is example of gson.
You need this dependency.
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.2'
And then convertion
Gson gson=new Gson();
Activity parsedActivity =gson.fromJson(jsonString, Activity.class);
If you have a collection (array) of activities, it goes the same way
Activity[] activities = gson.fromJson(jsonString, Activity[].class);
https://stackoverflow.com/a/17300003/4587961
If you use another library, rather than gson, here is an example of Jackson.
ObjectMapper mapper = new ObjectMapper();
String jsonInString = "{\n \"name\": \"enter_username\",\n\"longitude\": \"22.601952\",\n\"latitude\": \"40.674065\",\n\"type\": \"Big_Pothole\"\n}";//Your JSON String;
//JSON from file to Object
Activity activity = mapper.readValue(jsonInString, Activity.class);
https://www.mkyong.com/java/jackson-2-convert-java-object-to-from-json/
Or if you really want, you can create loop through json objects, as you do in your code example, and create Activity object one by one, and put them into the result collection. This guy has answered here.
https://stackoverflow.com/a/48439517/4587961

Converted json (from ArrayList) has more elements

I post what I have done, as I don't get the result.. Here I have a method which returns an ArrayList:
public ArrayList<Label> getLabels()
throws ClassNotFoundException, SQLException{
ArrayList<Label> labels = new ArrayList<>();
sq = "SELECT * from LABELS";
try {
Class.forName(typeDB);
c = DriverManager.getConnection(path);
stm = c.prepareStatement(sq);
ResultSet rs = stm.executeQuery();
while(rs.next()) {
Label label = new Label(rs.getString("type"), rs.getString("description"),rs.getString("product")+"-"+rs.getString("version"), rs.getString("cutter"));
labels.add(label);
}
} catch (SQLException e) {
System.out.println(e.getMessage());
} finally {
if (stm != null)
stm.close();
if (c != null)
c.close();
}
System.out.println("Label "+ labels.size());
return labels;
}
then I want covert this ArrayList to JSON format. So I execute labelsToJSON(action.getLabels()); where:
public void labelsToJSON(ArrayList<Label> list){
ObjectMapper mapper = new ObjectMapper();
try{
mapper.writeValue(new File("C:\\temp\\labels.json"), list);
}catch(JsonGenerationException e){
e.printStackTrace();
}catch(JsonMappingException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
}
The Label class is defined:
public class Label {
private String barcode;
private String labelCode;
private String productCode;
private String type;
//and many others..
public Label(){
}
//This is the costructor I use above in the method
public Label(String type, String description, String productCode, String cutter) {
this.type = type;
this.description = description;
this.productCode = productCode;
this.cutter = cutter;
}
//and then some other constructors (I post 2 for example)
public Label(String type, String description, String product, String version, String cutter) {
this.type = type;
this.description = description;
this.product = product;
this.version = version;
this.cutter = cutter;
}
public Label(String barcode, String product, String version, String dateProduction, String order , int quantity, String packetNumber, String type, String description, String cutter) {
this.barcode = barcode;
this.product = product;
this.version = version;
this.dateProduction = dateProduction;
this.order = order;
this.packetNumber = packetNumber;
this.quantity = quantity;
this.type = type;
this.description = description;
this.cutter = cutter;
}
//setters, getters etc
So, I create an object from the constructor with parameters String type, String description, String productCode, String cutter. However the labels.json contains these data
[{
"barcode":null,
"labelCode":null,
"productCode":"111123123-1123", //<-
"type":"Container", //<-
"description":"this is a description", //<- all these I was expected.
"cutter":"1031", //<-
"date":null,
"time":null,
"dateProduction":null,
"order":null,
"product":null,
"version":null,
"packetNumber":null,
"quantity":0
}, //and so on
I don't understand why the json file has so many attributes?? My objects supposed to have only 4 --> String type, String description, String productCode, String cutter
ObjectMapper will by default serialise all field values on a class, regardless of whether they are null or not so you get everything from your Label class.
To only serialise non null values that you can configure the ObjectMapper see the JavaDoc for setSerializationInclusion and Include
mapper.setSerializationInclusion(Include.NON_NULL);
EDIT:
As Maraboc pointed out you have the problem with quantity still being serialised when using the Include.NON_NULL. For finer grain control of which fields are serialised you can use #JsonIgnore annotation to prevent the other fields in your class from being serialised.
Or you can add #JsonIgnoreProperties({"quantity"}) to your class
You can define your Label class with JsonSerialize annotation and change the type of quantity from primitive int to Integer Object. If type is int, the default value zero will be assigned to the variable.
#JsonSerialize(
include=JsonSerialize.Inclusion.NON_NULL)
public class Label {
// ...other properties
private Integer quantity;
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
}

How I can add a property to a JSON was created GSON?

I have the following class
public class OrderShoppingCart {
#Expose
#SerializedName("id")
private String _id;
#Expose
#SerializedName("quantity")
private int _quantity;
private String _description;
private String _name;
private int _price;
#Expose
#SerializedName("selections")
private List<SelectionShoppingCart> _selections;
public OrderShoppingCart(String id, int quantity, String description, String name,int price, List<SelectionShoppingCart> selections) {
_id = id;
_quantity = Integer.valueOf(quantity);
_description = description;
_name = name;
_price = price;
_selections = selections;
}
//HERE GET AND SETTER
}
I built the GSON follows
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String jsonG = gson.toJson(OrderShoppingCart.getOrders());
//OrderShoppingCart.getOrders() return all atributes
And I got the following
[{"id":"525b207b16b1e9ca33000143","selections":[],"quantity":1}]
but I need this
{items:[{"id":"525b207b16b1e9ca33000143","selections":[],"quantity":1}]}
How I can add what I'm missing?
Try to create a JsonObject and add a member with name items and value your Json object.
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
JsonObject jObj = new JsonObject();
jObj.add("items", gson.toJson(OrderShoppingCart.getOrders()));
String jsonG = jObj.toString();

Categories

Resources