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.
Related
I have a class representing the root node and want to deserialize data to different subclasses based on the value of action. The fields must be final.
#EqualsAndHashCode
#ToString
public final class SeMessage {
#Getter
private final String action;
#Getter
private final SeMessageData data;
#JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public SeMessage(
#JsonProperty("action") final String action,
#JsonProperty("data")
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "action", include = JsonTypeInfo.As.EXTERNAL_PROPERTY)
#JsonSubTypes({
#JsonSubTypes.Type(value = Se155QuestionsActiveMessageData.class, name = "155-questions-active")
}) final SeMessageData data
) {
super();
this.action = action;
this.data = data;
}
}
Here are SeMessageData and Se155QuestionsActiveMessageData:
public abstract class SeMessageData {
SeMessageData() {
super();
}
}
#EqualsAndHashCode
#ToString
public final class Se155QuestionsActiveMessageData extends SeMessageData {
#Getter
private final String siteBaseHostAddress;
#Getter
private final Long id;
#Getter
private final String titleEncodedFancy;
#Getter
private final String bodySummary;
#Getter
private final List<String> tags;
#Getter
private final Long lastActivityDate;
#Getter
private final String url;
#Getter
private final String ownerUrl;
#Getter
private final String ownerDisplayName;
#Getter
private final String apiSiteParameter;
#JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Se155QuestionsActiveMessageData(
#JsonProperty("siteBaseHostAddress") final String siteBaseHostAddress,
#JsonProperty("id") final Long id,
#JsonProperty("titleEncodedFancy") final String titleEncodedFancy,
#JsonProperty("bodySummary") final String bodySummary,
#JsonProperty("tags") final List<String> tags,
#JsonProperty("lastActivityDate") final Long lastActivityDate,
#JsonProperty("url") final String url,
#JsonProperty("ownerUrl") final String ownerUrl,
#JsonProperty("ownerDisplayName") final String ownerDisplayName,
#JsonProperty("apiSiteParameter") final String apiSiteParameter
) {
super();
this.siteBaseHostAddress = siteBaseHostAddress;
this.id = id;
this.titleEncodedFancy = titleEncodedFancy;
this.bodySummary = bodySummary;
this.tags = tags;
this.lastActivityDate = lastActivityDate;
this.url = url;
this.ownerUrl = ownerUrl;
this.ownerDisplayName = ownerDisplayName;
this.apiSiteParameter = apiSiteParameter;
}
}
Topic #1:
Doing so causes an exception to be thrown:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.oliveryasuna.stackexchange.websocket.message.data.Se155QuestionsActiveMessageData` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"siteBaseHostAddress":"stackoverflow.com","id":70098765,"titleEncodedFancy":"How to avoid the command execution when appending lines to a file","bodySummary":"I'm trying to save the content of script into a file using command line, but I noticed that when the tee command detects linux commands such as $(/usr/bin/id -u), it execute the commands rather than ...","tags":["linux","append","tee"],"lastActivityDate":1637767762,"url":"https://stackoverflow.com/questions/70098765/how-to-avoid-the-command-execution-when-appending-lines-to-a-file","ownerUrl":"https://stackoverflow.com/users/17499564/alex","ownerDisplayName":"Alex","apiSiteParameter":"stackoverflow"}')
at [Source: (String)"{"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"stackoverflow.com\",\"id\":70098765,\"titleEncodedFancy\":\"How to avoid the command execution when appending lines to a file\",\"bodySummary\":\"I'm trying to save the content of script into a file using command line, but I noticed that when the tee command detects linux commands such as $(/usr/bin/id -u), it execute the commands rather than ...\",\"tags\":[\"linux\",\"append\",\"tee\"],\"lastActivityDate\":1637767762,\"url\":\"[truncated 248 chars]; line: 1, column: 748]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1588)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1213)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer._deserializeFromString(StdDeserializer.java:311)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1495)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:207)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:197)
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:120)
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromObject(AsArrayTypeDeserializer.java:61)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:263)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:539)
at com.fasterxml.jackson.databind.deser.impl.ExternalTypeHandler._deserialize(ExternalTypeHandler.java:359)
at com.fasterxml.jackson.databind.deser.impl.ExternalTypeHandler.complete(ExternalTypeHandler.java:302)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeUsingPropertyBasedWithExternalTypeId(BeanDeserializer.java:1090)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeWithExternalTypeId(BeanDeserializer.java:931)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:360)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:195)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3548)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3516)
at com.oliveryasuna.stackexchange.websocket.SeWebSocketHandler.handleTextMessage(SeWebSocketHandler.java:56)
at org.springframework.web.socket.handler.AbstractWebSocketHandler.handleMessage(AbstractWebSocketHandler.java:43)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:114)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:43)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:85)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:82)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:415)
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:515)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:301)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133)
at org.apache.tomcat.websocket.WsFrameClient.processSocketRead(WsFrameClient.java:95)
at org.apache.tomcat.websocket.WsFrameClient.resumeProcessing(WsFrameClient.java:212)
at org.apache.tomcat.websocket.WsFrameClient.access$500(WsFrameClient.java:31)
at org.apache.tomcat.websocket.WsFrameClient$WsFrameClientCompletionHandler.doResumeProcessing(WsFrameClient.java:189)
at org.apache.tomcat.websocket.WsFrameClient$WsFrameClientCompletionHandler.completed(WsFrameClient.java:163)
at org.apache.tomcat.websocket.WsFrameClient$WsFrameClientCompletionHandler.completed(WsFrameClient.java:148)
at org.apache.tomcat.websocket.AsyncChannelWrapperSecure$WrapperFuture.complete(AsyncChannelWrapperSecure.java:471)
at org.apache.tomcat.websocket.AsyncChannelWrapperSecure$ReadTask.run(AsyncChannelWrapperSecure.java:338)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)
I tried using a custom deserializer, but the same exception is thrown. You'll notice that this uses regex, which is my ultimate goal (see Topic #2).
final class Deserializer extends StdDeserializer<SeMessage> {
private static final Map<String, Class<? extends SeMessageData>> ACTION_TYPE_MAP = Map.ofEntries(
Map.entry("^155-questions-active$", Se155QuestionsActiveMessageData.class),
Map.entry("^(?!155)\\d+-questions-(?:active|newest(?:-tag-[a-z0-9]+)?)$", SeQuestionsMessageData.class)
);
private Deserializer() {
super(SeMessage.class);
}
#Override
public final SeMessage deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException {
final JsonNode json = parser.getCodec().readTree(parser);
final String action = json.get("action").textValue();
for(final Map.Entry<String, Class<? extends SeMessageData>> actionTypeEntry : ACTION_TYPE_MAP.entrySet()) {
final String actionRegex = actionTypeEntry.getKey();
if(action.matches(actionRegex)) {
final Class<? extends SeMessageData> type = actionTypeEntry.getValue();
final JsonNode dataJson = json.get("data");
final SeMessageData data = parser.getCodec().treeToValue(dataJson, type);
return new SeMessage(action, data);
}
}
throw new IOException("Unsupported action: " + action + ".");
}
}
Odd enough, if I deserialize data with the followingly (instead of treeToValue), no exception is thrown. The OBJECT_MAPPER is just a plain instance of ObjectMapper without any added modules or configuration.
final String dataJson = json.get("data").textValue();
final SeMessageData data = JacksonUtil.OBJECT_MAPPER.readValue(dataJson, type);
return new SeMessage(action, data);
Topic #2:
Once Topic #1 is resolved, I will still want to avoid using a custom deserializer. You'll notice in the custom deserialize class above that I match the value of action with regex. Is it possible to override the functionality of #JsonTypeInfo and #JsonSubTypes, such that the name argument is regex and the value of action is matched that way?
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 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.
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'm not clear how jackson deals with capitalization in mapping fields. If anyone could help I'd appreciate it.
{"user":{"username":"user#host.com","password":"pwd","sendercompid":"COMPID","service":{"host":"address","port":6666,"service":"S1","serviceAsString":"s1"}},"MDReqID":"ghost30022","NoRelatedSym":1,"Symbol":["GOOG"],"MarketDepth":"0","NoMDEntryTypes":3,"MDEntryType":["0","1","2"],"SubscriptionRequestType":"1","AggregatedBook":"N"}:
Above is my json, below is my exception...
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "MDReqID" (class com.myco.qa.fixrest.MarketDataRequest), not marked as ignorable (10 known properties: , "mdreqID", "marketDepth", "user", "subscriptionRequestType", "aggregatedBook", "mdentryType", "symbol", "mdupdateType", "noRelatedSym", "noMDEntryTypes"])
Above is my exception, below is my class...
public class MarketDataRequest {
private User user;
private String MDReqID;
private char SubscriptionRequestType;
private int MarketDepth;
private int MDUpdateType;
private char AggregatedBook;
private int NoMDEntryTypes;
private ArrayList<Character> MDEntryType;
private int NoRelatedSym;
private ArrayList<String> Symbol;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getMDReqID() {
return MDReqID;
}
public void setMDReqID(String MDReqID) {
this.MDReqID = MDReqID;
}
public char getSubscriptionRequestType() {
return SubscriptionRequestType;
}
public void setSubscriptionRequestType(char subscriptionRequestType) {
SubscriptionRequestType = subscriptionRequestType;
}
... et cetera
Since your setter method is named setMDReqID(…) Jackson assumes the variable is named mDReqID because of the Java naming conventions (variables should start with lower case letters).
If you really want a capital letter use the #JsonProperty annotation on the setter (or - for serialization - on the getter) like this:
#JsonProperty("MDReqID")
public void setMDReqID(String MDReqID) {
this.MDReqID = MDReqID;
}
You can also do
#JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)
on the class to capitalise all property names in the JSON message
Add #JsonProperty on the setter that matches the property name in your received JSON string:
#JsonProperty("MDReqID")
public void setMDReqID(String MDReqID) {
this.MDReqID = MDReqID;
}
Additionally add #JsonProperty annotation to the getter as well for your output to appear in the conventional format:
#JsonProperty("mDReqID")
public String getMDReqID() {
return MDReqID;
}
Now you can name your variable whatever you like:
private String mdReqID;
I solve this problem by:
#Getter
#Setter
static class UserInfo {
//#JsonProperty("UUID")
private String UUID = "11";
private String UserName = "22";
private String userName = "33";
private String user_Name = "44";
private String user_name = "55";
private String User_name = "66";
private boolean HasDeleted=true;
private boolean hasDeleted=true;
private boolean has_Deleted=true;
private boolean has_deleted=true;
private boolean HAS_DELETED=true;
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
String s = objectMapper.writeValueAsString(new UserInfo());
System.out.println(s);
UserInfo userInfo = objectMapper.readValue(s, UserInfo.class);
System.out.println(objectMapper.writeValueAsString(userInfo));
}
output:
{"UUID":"11","UserName":"22","userName":"33","user_Name":"44","user_name":"55","User_name":"66","HasDeleted":true,"hasDeleted":true,"has_Deleted":true,"has_deleted":true,"HAS_DELETED":true}
I face the same problem , after have try UpperCamelCaseStrategy but still this error occurred , the strategy made my field pContent to ObjectMapper property Pcontent, as not want to add #JsonProperty for every field, simply use gson instead at last
Use JsonNaming Annotation to get all Class Field Names in Proper Case
Use lombok.Data Annotation to automatically make it work without adding getters and setters in your class
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import lombok.Data;
#JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class)
#Data