Why is Jackson using the wrong element name when serializing? - java

I have a Object that I would like Jackson to serialize like this...
<AccountsResponse>
<accounts>
<account/>
<account>
<userId>user</userId>
...
</account>
</accounts>
</AccountsResponse>
To try this I create the following class...
#Getter
#Setter
#ToString
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Payload {
#JacksonXmlProperty(localName = "errormessage")
private String errorMessage;
}
#Getter
#Setter
#ToString
public class AccountsResponse extends Payload{
#JsonIgnore
private static Logger LOGGER = LogManager.getLogger(AccountsResponse.class);
#JacksonXmlProperty(localName = "accounts")
private List<Account> accounts = Lists.newArrayList();
public static AccountsResponse mapFromResultSet(ResultSet rs)
throws SQLException
{
AccountsResponse response = new AccountsResponse();
do {
Account acct = Account.mapFromResultSet(rs);
response.getAccounts().add(acct);
} while (rs.next());
return response;
}
public String toXml() throws JsonProcessingException {
ObjectMapper mapper = new XmlMapper();
return mapper.writeValueAsString(this);
}
}
#Getter
#Setter
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Account extends ResultSetParser{
...
}
But when I serialize I get...
<AccountsResponse>
<accounts>
<accounts/>
<accounts>
<userId>user</userId>
...
</accounts>
</accounts>
</AccountsResponse>
As you can see the problem here is the child tags should be account but in fact are accounts. I tried hacking around with the localname but can't find the right mixture of VooDoo. What am I doing wrong?

I would change annotations on account list in AccountsResponse:
public class AccountsResponse extends Payload{
#JacksonXmlElementWrapper(localName = "accounts")
#JacksonXmlProperty(localName = "account")
private List<Account> accounts = Lists.newArrayList();
}

Related

Using Java records for (de-)serializing complex/nested objects to JSON

Given the following JSON...
{
"loginData": {
"username": "foobar",
"password": "secret"
},
"personalData": {
"givenName": "Foo",
"familyName": "Bar"
}
}
This is what my POJO (with some maybe verbose Lombok annotations) looks like. It gets (de-)serialized by Jackson:
#Builder
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class User {
private LoginData loginData;
private PersonalData personalData;
#Builder
#Getter
#NoArgsConstructor
#AllArgsConstructor
public static class LoginData {
private String username;
private String password;
}
#Builder
#Getter
#NoArgsConstructor
#AllArgsConstructor
public static class PersonalData {
private String givenName;
private String familyName;
}
}
I wonder if it is possible to replicate this structure using the relatively new concept of a Java record. I'm aware of a simple record like:
public record LoginData(String username, String password) {}
But can records also be used for nested/complex objects which shall be converted to JSON with Jackson?
From what I understood you can use records similar to normal objects/classes. This also means you can use records as parameters for other records. I created a simple example for converting your input JSON string to a nested object with records:
Interface defining three records:
public interface IRecord {
record LoginData(String username, String password) {};
record PersonalData(String givenName, String familyName) {};
record Data(LoginData loginData, PersonalData personalData) {};
}
Main class to test:
public class Main {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
String jsonInString = "{\"loginData\": {\"username\": \"foobar\",\"password\": \"secret\"}, \"personalData\": {\"givenName\": \"Foo\", \"familyName\": \"Bar\"}}";
IRecord.Data user = mapper.readValue(jsonInString, IRecord.Data.class);
System.out.println(user.loginData().username());
}
}
Output:
foobar

A Facebook Messenger BOT With Java

I try to create a facebook messenger bot
I tried Victor de la Cruz method,the code is below
, and here is the origin code:
https://www.codementor.io/#vcg_cruz/a-facebook-messenger-bot-m2g6wgcxb#comments-m2g6wgcxb
it works but when I send a message it generates an infinite loop of conversation
this the result screen shot
#RestController()
#RequestMapping("webhook")
public class WebHook {
private final String PAGE_TOKEN ="THIS_IS_THE_TOKEN_YOU_COPIED_BEFORE";
private final String VERIFY_TOKEN="A_SECRET_VERIFY_TOKEN";
//this is for reply messages
private final String FB_MSG_URL="https://graph.facebook.com/v2.6/me/messages?access_token="
+ PAGE_TOKEN;
//logger to watch whats happening in our bot
private final Logger logger = LoggerFactory.getLogger(WebHook.class);
private final RestTemplate template = new RestTemplate();
//This is necessary for register a webhook in facebook
#GetMapping()
#ResponseStatus(HttpStatus.OK)
public String get(#RequestParam(name = "hub.verify_token")String token,
#RequestParam(name = "hub.challenge")String challenge){
if(token!=null && !token.isEmpty() && token.equals(VERIFY_TOKEN)){
return challenge;
}else{
return "Wrong Token";
}
}
//This method reply all messages with: 'This is a test message'
#PostMapping
#ResponseStatus(HttpStatus.OK)
public void post(#RequestBody FacebookHookRequest request){
logger.info("Message from chat: {}",request);
request.getEntry().forEach(e->{
e.getMessaging().forEach(m->{
String id = m.getSender().get("id");
sendReply(id,"This is a test message");
});
});
}
private void sendReply(String id,String text){
FacebookMessageResponse response = new FacebookMessageResponse();
response.setMessage_type("text");
response.getRecipient().put("id",id);
response.getMessage().put("text",text);
HttpEntity<FacebookMessageResponse> entity = new HttpEntity<>(response);
String result = template.postForEntity(FB_MSG_URL,entity,String.class).getBody();
logger.info("Message result: {}",result);
}
}
/************************************/
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class FacebookEntry implements Serializable {
private String id;
private Long time;
private List<FacebookMessaging> messaging = new ArrayList<>();
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class FacebookHookRequest implements Serializable {
private String object;
private List<FacebookEntry> entry = new ArrayList<>();
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class FacebookMessage implements Serializable {
private String mid;
private Long seq;
private String text;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class FacebookMessageResponse implements Serializable {
private String message_type;
private Map<String,String> recipient = new HashMap<>();
private Map<String,String> message = new HashMap<>();
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class FacebookMessaging implements Serializable {
private Map<String,String> sender;
private Map<String,String> recipient;
private Long timestamp;
private FacebookMessage message;
}
You're responding to yourself I assume. Similar to how the Discord API works, your post method is being called when you receive your own message. You most likely want to check if the message author is from yourself.

GSON StackOverFlow Error- Serialization when using Lombok with Inheritance

I have a POJO that extends another class ParentDTO both using Lombok with builder Pattern 
ChildDTO
 
#Getter
#ToString
#EqualsAndHashCode(callSuper = false)
#SuperBuilder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public class ChildDTO extends ParentDTO {
private final Foo foo; // Foo also extends ParentDTO
#Singular("items")
private final List<Item> items;  
}
ParentDTO
#Data
  
#SuperBuilder(toBuilder = true)
public abstract class ParentDTO implements Serializable {
 
#JsonIgnore private final ObjectMapper objectMapper = new ObjectMapper();
 
protected ParentDTO () {} 
}
            
My Code Implementation(here I am getting error in gson.toJson)
#PostMapping("/sendRequest")
public ResponseEntity<String> sendMessage(#RequestBody ChildDTO payload) {
final ChildDTO childDTO = payload.toBuilder().bar("xyz").build();
String serializedData = gson.toJson(childDTO); //getting stackoverflow exception in this line.
publishMesaage(serializedData);
return new ResponseEntity<>(HttpStatus.OK);
}
I am seeing the stackoverflow error happening in line gson.toJson(childDTO) when trying to use Gson.
java.lang.StackOverflowError
at java.lang.System.arraycopy(Native Method)
at java.lang.String.getChars(String.java:826)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:449)
at java.lang.StringBuffer.append(StringBuffer.java:270)
at java.io.StringWriter.write(StringWriter.java:112)
at com.google.gson.stream.JsonWriter.string(JsonWriter.java:590)
at com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:401)
at com.google.gson.stream.JsonWriter.beginObject(JsonWriter.java:308)
at

ModelMapper: Incorect property mapping from null objects

I'm trying to map source object which property is set to null to destination object of which this property is set to another object.
Expected result would be that property of destination object will be null after mapping. Instead of that, this property is set to an object and all of its properties are set to null.
Here is an example:
public class ModelMapperTest {
public static void main(String[] args) {
ModelMapper modelMapper = new ModelMapper();
User user = new User();
user.setStatus(null);
StatusDto statusDto = new StatusDto();
statusDto.setId(1);
statusDto.setName("Active");
UserDto userDto = new UserDto();
userDto.setStatus(statusDto);
// user.status=null, userDto.status=StatusDto(id=1, name="Active")
modelMapper.map(user, userDto);
System.out.println("user = " + user);
System.out.println("userDto = " + userDto);
}
#Getter
#Setter
#ToString
public static class User {
private Status status;
}
#Getter
#Setter
#ToString
public static class Status {
private Integer id;
private String name;
}
#Getter
#Setter
#ToString
public static class UserDto {
private StatusDto status;
}
#Getter
#Setter
#ToString
public static class StatusDto {
private Integer id;
private String name;
}
}
Output:
user = ModelMapperTest.User(status=null)
userDto = ModelMapperTest.UserDto(status=ModelMapperTest.StatusDto(id=null, name=null))
Is it possible to somehow configure model mapper to sets UserDto.status to null?
I know this is an older question and you seem to have moved on to a different library, but I had the same problem recently and came up with this solution (building on your example):
Converter<?, ?> preserveNullConverter = (context) ->
context.getSource() == null
? null
: modelMapper.map(context.getSource(), context.getDestinationType());
modelMapper.createTypeMap(User.class, UserDto.class)
.addMappings(mapper -> mapper.using(preserveNullConverter).map(User::getStatus, UserDto::setStatus));
It's not ideal because the .addMappings part needs to be done for every property where the issue occurs, but at least the Converter can be reused.

Spring mapper with Java Map

I've been struggling with the following issue for a few hours now, and I can't figure it out how to make it work:
Spring mapper, in order to convert DB response to DTO:
#Mapper(componentModel = "spring")
public interface ITeamResponseToDtoMapper {
TeamResponseDTO toDto(TeamResponse teamResponse);
}
TeamResponse class:
#Data
#NoArgsConstructor
public class TeamResponse {
private Map<String, List<NameAndType>> teamList;
}
NameAndType class:
#Data
#NoArgsConstructor
#AllArgsConstructor(access = AccessLevel.PUBLIC)
public class NameAndType{
private String name;
private String type;
private String team;
}
TeamResponseDTO class:
#Data
#NoArgsConstructor
public class TeamResponseDTO {
private Map<String, List<NameAndTypeDTO >> teamList;
}
NameAndTypeDTO class:
#Data
#NoArgsConstructor
#AllArgsConstructor(access = AccessLevel.PUBLIC)
public class NameAndTypeDTO {
private String name;
private String type;
private String team;
}
Basically, 'NameAndType' and 'NameAndTypeDTO' are the same, why it fails to do the conversion?
error: Can't map property "java.util.Map<java.lang.String,java.util.List<com.microservices.teamservice.dataobjects.NameAndType>> teamList" to "java.util.Map<java.lang.String,java.util.List<com.microservices.teamservice.api.dataobjects.NameAndTypeDTO>> teamList". Consider to declare/implement a mapping method:
I think you need to explicit add methods to map the whole chain of classes. On your example the following should work:
#Mapper(componentModel = "spring")
public interface ITeamResponseToDtoMapper {
TeamResponseDTO toDto(TeamResponse teamResponse);
List<NameAndTypeDTO> natListToDTO(List<NameAndType> natList);
NameAndTypeDTO nameAndTypeToDTO(NameAndType nameAndType);
}
regards,
WiPu

Categories