I get a JSON-Object as an array form an external source which looks like this:
[13823108,"Text1","Text2",null,null,1585730520000,1585753742000,null,null,"COMPLETED",null]
The array will always have null values at those positions, which is why I would like to ignore those.
My Java object is suppose to look like this:
#JsonFormat(shape = JsonFormat.Shape.ARRAY)
public class MyJsonObject {
private final String id;
private final String textField1;
private final String textField2;
private final Date started;
private final Date updated;
private final String status;
#JsonCreator
public MyJsonObject(
#JsonProperty("id") final String id,
#JsonProperty("textField1") final String textField1,
#JsonProperty("textField2") final String textField2,
#JsonProperty("started") final Date started,
#JsonProperty("updated") final Date updated,
#JsonProperty("status") final String status) {
this.id = id;
this.textField1 = textField1;
this.textField2 = textField2;
this.started = started;
this.updated = updated;
this.status = status;
}
[...]
}
When I try to unmarshal the Json the null values are used, which courses the additional values to be ignored:
MyJsonObject{id='13105603', textField1='Text1', textField2='Text2', started=null, updated=null, status='1569348774000'}
Is there a way to tell the ObjectMapper to ignore null values of the array? Can I achieve this without writing a specific Deserializer?
I already tried #JsonInclude(JsonInclude.Include.NON_NULL) with no effect.
Related
I need to get some specific data from rest to my model.
Data I get from rest :
[{"id":595,"name":"TransXChange20210805113332","prosystem_file_id":null,"dataset":16,"creator":113,"editor":113,"created":"2021-08-05T09:45:21.444949Z","edited":"2021-08-05T09:45:27.542152Z","update_from_url":false,"description":"Rozkłady jazdy komunikacji miejskiej ważne od 08.08.2021","file":"https://otwartedane.erzeszow.pl/media/resources/transxchange20210805113332.xml","link":null,"extension":"XML","data_type":"[]","file_name":"transxchange20210805113332.xml","chart_info":null,"map_info":null,"score":4,"public":true,"licence":"other-open","connection_dict":null,"selection_string":null,"db_type":"POSTGRESQL","type":"file","dbview_cache_minutes":1,"preview_base64":null,"gpkg_display_info":null,"archive_to_csv":false},{"id":538,"name":"TransXChange20210513082611","prosystem_file_id":null,"dataset":16,"creator":113,"editor":113,"created":"2021-05-13T06:28:50.233464Z","edited":"2021-07-28T08:52:06.695966Z","update_from_url":false,"description":"Rozkłady jazdy komunikacji miejskiej ważne od 15.05.2021","file":"https://otwartedane.erzeszow.pl/media/resources/transxchange20210513082611.xml","link":null,"extension":"XML","data_type":"[]","file_name":"transxchange20210513082611.xml","chart_info":null,"map_info":null,"score":4,"public":true,"licence":"other-open","connection_dict":null,"selection_string":null,"db_type":"POSTGRESQL","type":"file","dbview_cache_minutes":1,"preview_base64":null,"gpkg_display_info":null,"archive_to_csv":false},{"id":544,"name":"TransXChange20210526143716","prosystem_file_id":null,"dataset":16,"creator":113,"editor":113,"created":"2021-05-26T12:40:42.587492Z","edited":"2021-07-28T08:52:04.417450Z","update_from_url":false,"description":"Rozkłady jazdy komunikacji miejskiej ważne od 01.06.2021","file":"https://otwartedane.erzeszow.pl/media/resources/transxchange20210526143716.xml","link":null,"extension":"XML","data_type":"[]","file_name":"transxchange20210526143716.xml","chart_info":null,"map_info":null,"score":4,"public":true,"licence":"other-open","connection_dict":null,"selection_string":null,"db_type":"POSTGRESQL","type":"file","dbview_cache_minutes":1,"preview_base64":null,"gpkg_display_info":null,"archive_to_csv":false}]
i got it in single line
my code in java :
RestTemplateBuilder builder = new RestTemplateBuilder();
String soc = builder.build().getForObject("https://otwartedane.erzeszow.pl/v1/datasets/16/resources/", String.class);
assert soc != null;
System.out.println(soc);
And the problem is I need to put them into model
My model:
public class Resource {
private long id;
private String name;
private long prosystem_file_id;
private int dataset;
private int creator;
private int editor;
private LocalDateTime created;
private LocalDateTime edited;
private boolean update_from_url;
private String description;
private String file;
private String link;
private String extension;
private String data_type;
private String file_name;
private String chart_info;
private String map_info;
private int score;
private boolean isPublic;
private String licence;
private String connection_dict;
private String selection_string;
private String db_type;
private String type;
private int dbview_cache_minutes;
private String preview_base64;
private String gpkg_display_info;
private boolean archive_to_csv;
All getters and setters are generated.
How can I put them into model for example List?
The problem is I get data like [{resource},{resource},{resource}].
As it states from the RestTemplate documentation
you can call to the getForObject method passing as an argument the desired deserialization class.
Something like:
RestTemplateBuilder builder = new RestTemplateBuilder();
Resource soc = builder.build().getForObject("https://otwartedane.erzeszow.pl/v1/datasets/16/resources/", Resource.class);
assert soc != null;
System.out.println(soc);
I have found answer to my question.
I need to assign my call to array of model.
Resource[] soc =builder.build().getForObject( URL , Resource[].class );
I'm trying to convert the following Mongo query for use with Spring data.
db.product.aggregate([
{$unwind: '$barcodes'},
{$project: {
_id: 0,
productId: '$_id',
productTitle: '$title',
productVariation: '$variation',
barcode: '$barcodes'
}}])
This is what I've been trying so far. It returns the aggregation, but with null values:
UnwindOperation unwindOperation = Aggregation.unwind("barcodes");
ProjectionOperation projectStage = Aggregation.project().and("productId").as("_id").and("productTitle")
.as("title")
.and("productVariation").as("variation")
.and("barcodeTitle").as("barcodes.title")
.and("barcodeValue").as("barcodes.value")
.and("barcodeType").as("barcodes.type")
.and("codeStandard").as("barcodes.codeStandard")
.and("quantity").as("barcodes.quantity")
.and("status").as("barcodes.status");
SortOperation sortOperation = Aggregation.sort(Sort.by(Sort.Direction.DESC, "title"));
Aggregation agg = Aggregation.newAggregation(unwindOperation, projectStage, sortOperation);
AggregationResults<BarcodeAggregateList> results = mongoTemplate.aggregate(agg, "product", BarcodeAggregateList.class);
What it is returning:
The class I am mapping to (has getters/setters):
public class BarcodeAggregateList {
private String productId;
private String productTitle;
private String productVariation;
private String barcodeTitle;
private String barcodeValue;
private String barcodeType;
private String codeStandard;
private int quantity;
private String status;
}
Product class that the data is coming from:
public class Product implements Serializable {
private static final long serialVersionUID = -998149317494604215L;
private String id;
private String title;
private String description;
private String SKU;
private double cost;
private double retailPrice;
private String status;
private LocalDate launchDate;
private LocalDate discontinueDate;
private String discontinueReason;
private String salesChannel;
private List<Barcode> barcodes;
private ProductVariation variation;
private List<Supplier> supplier;
private Product parentProduct;
private boolean updateChildren;
private Label label;
private int secondaryStockLevel;
private int primaryStockLevel;
private Date createdDate;
private Date modifiedDate;
private List<Dimension> dimensions;
private boolean isDeleted = false;
}
Barcode class
public class Barcode {
private String type;
private String title;
private String value;
private String status;
private String codeStandard;
private int quantity;
}
I appreciate any help with this or resources to help me better understand how to perform these types of conversions.
For anyone trying to solve similar issues, I've found the following resources somewhat helpful:
https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo.query
https://xpadro.com/2016/04/data-aggregation-with-spring-data-mongodb-and-spring-boot.html
https://www.tutorialspoint.com/get-fields-from-multiple-sub-documents-that-match-a-condition-in-mongodb
BarcodeAggregateList class fields are null because there is a minor issue in ProjectionOperation's and() and as() methods. The correct syntax is
Aggregation.project().and(SOURCE_FIELD).as(TARGET_FIELD)
You have written and("productId").as("_id") , which is wrong
You need to write this as and("_id").as("productId") , because source field is _id
complete code:
UnwindOperation unwindOperation = Aggregation.unwind("barcodes");
ProjectionOperation projectStage = Aggregation.project()
.and("_id").as("productId")
.and("title").as("productTitle")
.and("variation").as("productVariation")
.and("barcodes.title").as("barcodeTitle")
.and("barcodes.value").as("barcodeValue")
.and("barcodes.type").as("barcodeType")
.and("barcodes.codeStandard").as("codeStandard")
.and("barcodes.quantity").as("quantity")
.and("barcodes.status").as("status");
SortOperation sortOperation = Aggregation.sort(Sort.by(Sort.Direction.DESC, "productTitle"));
Aggregation agg = Aggregation.newAggregation(unwindOperation, projectStage, sortOperation);
AggregationResults<BarcodeAggregateList> results = mongoTemplate.aggregate(agg, "product", BarcodeAggregateList.class);
I am trying to parse this json
{
"total_count": 3,
"offset": 0,
"limit": 50,
"notifications": [
{
"isEdge": false,
"large_icon": "",
"priority": 10,
"queued_at": 1579590097,
"remaining": 0,
"send_after": 1579590097,
"completed_at": 1579590110,
"small_icon": "",
"successful": 1,
"received": 1,
"tags": null,
"filters": null,
"template_id": null,
"ttl": null,
"url": "",
"contents": {
"en": "MoMo House presents MoMo eating competition. Come join us on Jan 26th
for the biggest MoMo eating competition ever."
}
"web_url": null,
"app_url": null,
"web_buttons": null,
"web_push_topic": null,
"wp_sound": "",
"wp_wns_sound": "",
"ios_attachments": null
}
]
}
This is my code to parse it
public ResponseEntity<HashMap<String,List<Notification>>> getAllNotifications(){
RestTemplate restTemplate = new RestTemplate();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Authorization",Constants.ONESIGNAL_REST_KEY);
HttpEntity<String> entity = new HttpEntity<String>("body", httpHeaders);
ResponseEntity<OneSignalResponse> oneSignalResponseResponseEntity = restTemplate.exchange(Constants.ONESIGNAL_URL, HttpMethod.GET,entity,OneSignalResponse.class);
OneSignalResponse oneSignalResponse=oneSignalResponseResponseEntity.getBody();
System.out.println("Title "+oneSignalResponse.getNotifications().get(0).getHeadings().getEn());
List<Notification> notifications = new ArrayList<>();
for(NotificationsItem oneSig : oneSignalResponse.getNotifications()){
Notification notification = new Notification();
notification.setId(oneSig.getId());
notification.setTitle(oneSig.getHeadings().getEn());
notification.setMessage(oneSig.getContents().getEn());
System.out.println("value of sent time "+oneSig.getCompletedAt()); //this is null
Date date = new Date(sentTime);
notification.setDate(localDateTime.toString());
notifications.add(notification);
}
HashMap<String,List<Notification>> hashMap = new HashMap<>();
hashMap.put("notifications",notifications);
return new ResponseEntity<>(hashMap, HttpStatus.OK);
}
And this is my POJO
public class NotificationsItem{
#SerializedName("isEdge")
private boolean isEdge;
#SerializedName("delayed_option")
private String delayedOption;
#SerializedName("queued_at")
private int queuedAt;
#SerializedName("failed")
private int failed;
#SerializedName("priority")
private int priority;
#SerializedName("remaining")
private int remaining;
#SerializedName("tags")
private Object tags;
#SerializedName("canceled")
private boolean canceled;
#SerializedName("chrome_icon")
private String chromeIcon;
#SerializedName("completed_at")
private Object completedAt;
#SerializedName("isWP")
private boolean isWP;
#SerializedName("send_after")
private int sendAfter;
#SerializedName("adm_sound")
private String admSound;
#SerializedName("chrome_web_image")
private String chromeWebImage;
#SerializedName("contents")
private Contents contents;
#SerializedName("converted")
private int converted;
#SerializedName("android_visibility")
private int androidVisibility;
#SerializedName("headings")
private Headings headings;
#SerializedName("adm_group")
private String admGroup;
#SerializedName("chrome_web_badge")
private String chromeWebBadge;
#SerializedName("spoken_text")
private SpokenText spokenText;
#SerializedName("ios_badgeCount")
private int iosBadgeCount;
#SerializedName("isChrome")
private boolean isChrome;
#SerializedName("alexa_ssml")
private Object alexaSsml;
#SerializedName("alexa_display_title")
private Object alexaDisplayTitle;
#SerializedName("android_accent_color")
private String androidAccentColor;
#SerializedName("android_group")
private String androidGroup;
#SerializedName("include_external_user_ids")
private Object includeExternalUserIds;
#SerializedName("isAdm")
private boolean isAdm;
#SerializedName("successful")
private int successful;
#SerializedName("web_push_topic")
private Object webPushTopic;
#SerializedName("adm_large_icon")
private String admLargeIcon;
#SerializedName("content_available")
private boolean contentAvailable;
#SerializedName("adm_small_icon")
private String admSmallIcon;
#SerializedName("buttons")
private Object buttons;
#SerializedName("app_url")
private Object appUrl;
#SerializedName("ios_badgeType")
private String iosBadgeType;
#SerializedName("delivery_time_of_day")
private String deliveryTimeOfDay;
#SerializedName("large_icon")
private String largeIcon;
#SerializedName("big_picture")
private String bigPicture;
#SerializedName("filters")
private Object filters;
#SerializedName("ttl")
private Object ttl;
#SerializedName("url")
private String url;
#SerializedName("include_player_ids")
private Object includePlayerIds;
#SerializedName("android_led_color")
private String androidLedColor;
#SerializedName("isWP_WNS")
private boolean isWPWNS;
#SerializedName("web_url")
private Object webUrl;
#SerializedName("adm_group_message")
private AdmGroupMessage admGroupMessage;
#SerializedName("adm_big_picture")
private String admBigPicture;
#SerializedName("small_icon")
private String smallIcon;
#SerializedName("template_id")
private Object templateId;
#SerializedName("web_buttons")
private Object webButtons;
#SerializedName("isAndroid")
private boolean isAndroid;
#SerializedName("isIos")
private boolean isIos;
#SerializedName("errored")
private int errored;
//getters setters
}
As the json is large I have included only those that matters and the POJO might not be accordingly here.
So when I call the method, I get the value of contents but I get null when I try to get completedAt this is shown as comment in the code too. So I am able to parse some parts perfectly but I am not able to parse some. I don't know what I am doing wrong so any help would be highly appreciated. Thanks
#SerializedName is a Gson annotation. Spring normally uses Jackson. The Jackson annotation for this would be #JsonProperty("name").
You have to change the datatype for the variable completedAt
#SerializedName("completed_at")
private Object completedAt;
To
#SerializedName("completed_at")
private Long completedAt;
I started using Project Reactor recently and I can't work out how to work with nested streams. I want to update data of outer Mono with some data of inner Mono.
#GetMapping("/search")
public Mono<Github> combineGithubData() {
WebClient client = WebClient.create("https://api.github.com");
Mono<Github> data = client.get().uri(URI.create("https://api.github.com/users/autocorrectoff")).retrieve().bodyToMono(Github.class);
data = data.map(s -> {
client.get().uri(URI.create("https://api.github.com/users/Kukilej")).retrieve().bodyToMono(Github.class).map(m -> {
s.setXXX(m.getName());
return m;
});
return s;
});
return data;
}
The field XXX is always returned as null, although I have set it to a value from inner Mono. I'm pretty sure this would work in RxJs. How do I make this work with Project Reactor?
edit:
the code of the Github class
import lombok.*;
#Getter #Setter
#Builder
#ToString
#NoArgsConstructor
#AllArgsConstructor
public class Github {
private String login;
private int id;
private String node_id;
private String avatar_url;
private String gravatar_id;
private String url;
private String html_url;
private String followers_url;
private String following_url;
private String gists_url;
private String starred_url;
private String subscriptions_url;
private String organizations_url;
private String repos_url;
private String events_url;
private String received_events_url;
private String type;
private boolean site_admin;
private String name;
private String company;
private String blog;
private String location;
private String email;
private String hireable;
private String bio;
private int public_repos;
private int public_gists;
private int followers;
private int following;
private String created_at;
private String updated_at;
private String XXX;
}
Your inner stream is not getting subscribed to. Either us flatMap, or better yet, use zip:
data
.zipWith(client.get().uri(...).retrieve().bodyToMono(Github.class))
.map(tuple2 -> {
//update tuple2.getT1() with m.getName() and return the updated tuple
return tuple2.mapT1(tuple2.getT1().setXXX(tuple2.getT2().getName()));
})
.map(tuple2 -> tuple2.getT1() //updated s);
zipWith() subscribes to the inner stream.
I'd love to use this:
#Getter
#ToString
#RequiredArgsConstructor(onConstructor_ = {#JsonCreator(mode = JsonCreator.Mode.PROPERTIES)})
private static class RestErrorObject {
private final String error; // optional
private final String message; // optional
private final String path; // optional
private final String status; // optional
private final String timestamp; // optional
}
But instead, I have to use this:
#Getter
#ToString
private static class RestErrorObject {
private final String error; // optional
private final String message; // optional
private final String path; // optional
private final String status; // optional
private final String timestamp; // optional
#JsonCreator
RestErrorObject(#JsonProperty("error") String error, #JsonProperty("message") String message,
#JsonProperty("path") String path, #JsonProperty("status") String status,
#JsonProperty("timestamp") String timestamp) {
this.error = error;
this.message = message;
this.path = path;
this.status = status;
this.timestamp = timestamp;
}
}
Is there any way I can use Lombok's RequiredArgsConstructor annotation with Jackson's JsonCreator? The problem appears to be the age-old Jackson requirement that each parameter in a multi-arg constructor be annotated with #JsonProperty. I understand this is a Java thing (or at least a Java 8 thing) that Jackson can't determine the argument names via reflection so the annotations must exist so Jackson knows where to pass each field from the json into the constructor. It just seems sadly redundant.
I had the same issue that you, found the solution here
https://projectlombok.org/features/constructor
To put annotations on the generated constructor, you can use
onConstructor=#__({#AnnotationsHere}), but be careful; this is an
experimental feature. For more details see the documentation on the
onX feature.
#Getter
#ToString
#RequiredArgsConstructor(onConstructor=#__(#JsonCreator(mode = JsonCreator.Mode.PROPERTIES)))
private static class RestErrorObject {
private final String error; // optional
private final String message; // optional
private final String path; // optional
private final String status; // optional
private final String timestamp; // optional
}
Even that I found no reference to this #__(...), I assume it converts the annotation to a constant for the compiler.