GSON,AndroidAnnotations - Expected BEGIN_OBJECT but was String - gson

I searched similar topics but none of them helped me.
My JSON response is:
{
"success": "true",
"data": {
"id": "x",
"user_name": "xxx",
"email": "xxx#xxx.com",
"first_name": "xxx",
"last_name": "xx",
"position": "xxx",
"session_id": "xxx"
}
}
My Java classes are:
Response:
public class Response {
public String success;
public Data data;
public Response() {
}
public Response(String success, Data data) {
this.success = success;
this.data = data;
}
}
Data
public class Data {
public String id;
public String user_name;
public String email;
public String first_name;
public String last_name;
public String position;
public String session_id;
public Data() {
}
public Data(String id, String user_name, String email, String first_name, String last_name, String position, String session_id) {
this.id = id;
this.user_name = user_name;
this.email = email;
this.first_name = first_name;
this.last_name = last_name;
this.position = position;
this.session_id = session_id;
}
}
I am using android annotations to establish rest connection.
My RestClient is:
#Rest(rootUrl = "http://xxx/services", converters = {GsonHttpMessageConverter.class})
public interface MyRestClient {
#Post("/login.php")
ResponseEntity<Response> login(User user);
RestTemplate getRestTemplate();
void setRestTemplate(RestTemplate restTemplate);
}
And in main activity I use:
ResponseEntity<Response> resp = restCli.login(new User("xxx","xxx"));
I get an error
Expected BEGIN_OBJECT but was String at line 1 column 4
I tried to change 'success' filed type to boolean,Boolean i Java class - didn't help.
I tried changing the method return type in the rest interface to void and then no error, so I think the error is connected with wrong response class, but I have no idea what is wrong. Could you help me?

Related

springboot - Json object return invalid

Currently, I am using rest template to get data from external api in getter method. While printing in console log, valid json format is coming. But, through postman output, invalid json output is coming (with \"). Pls suggest.
public class Product {
#Id
public Integer id;
private String name;
public Product() {
}
public Product(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
final String uri = "http://example.com/";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri + this.getId(), String.class);
System.out.println(result);
return result;
}
public void setName(String name) {
this.name = name;
}
}
//console.log (example)
{"product":{"rating_and_review_reviews":{"hasErrors":false,"offset":0,
"totalResults":0,"locale":"en_US","limit":10,"duration":1,"result":
[]},"question_answer_statistics"
// POSTMAN - HTTP GET (coming with \)
{
"id": 1,
"name": "{\"product\":{\"rating\":{\"hasErrors\":false,\"offset\":0,
\"totalResults\":0,\"locale\":\"en_US\",\"limit\":10,
\"duration\":1,\"result\":[]}

Nested Group with Spring MongoDB

I need to generate a result with the number of alerts of each level for each user.
A structure similar to the following:
{
"identitity": "59e3b9dc5a3254691f327b67",
"alerts": [
{
"level": "INFO",
"count": "3"
},
{
"level": "ERROR",
"count": "10"
}
]
}
The alert entitity has the following structure:
#Document(collection = AlertEntity.COLLECTION_NAME)
public class AlertEntity {
public final static String COLLECTION_NAME = "alerts";
#Id
private ObjectId id;
#Field
private AlertLevelEnum level = AlertLevelEnum.INFO;
#Field("title")
private String title;
#Field("payload")
private String payload;
#Field("create_at")
private Date createAt = new Date();
#Field("delivered_at")
private Date deliveredAt;
#Field("delivery_mode")
private AlertDeliveryModeEnum deliveryMode =
AlertDeliveryModeEnum.PUSH_NOTIFICATION;
#Field("parent")
#DBRef
private ParentEntity parent;
#Field("son")
#DBRef
private SonEntity son;
private Boolean delivered = Boolean.FALSE;
}
I have implemented the following method tried to project the result in a nested way. But the "Identity" field is always null and the "alerts" field is a empty collection.
#Override
public List<AlertsBySonDTO> getAlertsBySon(List<String> sonIds) {
TypedAggregation<AlertEntity> alertsAggregation =
Aggregation.newAggregation(AlertEntity.class,
Aggregation.group("son.id", "level").count().as("count"),
Aggregation.project().and("son.id").as("id")
.and("alerts").nested(
bind("level", "level").and("count")));
// Aggregation.match(Criteria.where("_id").in(sonIds)
AggregationResults<AlertsBySonDTO> results = mongoTemplate.
aggregate(alertsAggregation, AlertsBySonDTO.class);
List<AlertsBySonDTO> alertsBySonResultsList = results.getMappedResults();
return alertsBySonResultsList;
}
The result I get is the following:
{
"response_code_name": "ALERTS_BY_SON",
"response_status": "SUCCESS",
"response_http_status": "OK",
"response_info_url": "http://yourAppUrlToDocumentedApiCodes.com/api/support/710",
"response_data": [
{
"identity": null,
"alerts": []
},
{
"identity": null,
"alerts": []
}
],
"response_code": 710
}
The result DTO is as follows:
public final class AlertsBySonDTO implements Serializable {
private static final long serialVersionUID = 1L;
#JsonProperty("identity")
private String id;
#JsonProperty("alerts")
private ArrayList<Map<String, String>> alerts;
public AlertsBySonDTO() {
super();
}
public AlertsBySonDTO(String id, ArrayList<Map<String, String>> alerts) {
super();
this.id = id;
this.alerts = alerts;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public ArrayList<Map<String, String>> getAlerts() {
return alerts;
}
public void setAlerts(ArrayList<Map<String, String>> alerts) {
this.alerts = alerts;
}
}
What needs to be done to project the result in a nested way?
Thanks in advance
In aggregation framework there is an $unwind operator which will basically transform your one element collection with nested array of two elements to two separate documents with one element from this array. So you'll get:
{
"identitity": "59e3b9dc5a3254691f327b67",
"alerts": {
"level": "INFO",
"count": "3"
}
}
{
"identitity": "59e3b9dc5a3254691f327b67",
"alerts": {
"level": "ERROR",
"count": "10"
}
}
And this is where you can start your group by with count. Should be working fine.

Expose enums with Spring Data REST

I'm using Spring Boot 1.5.3, Spring Data REST, HATEOAS.
I've a simple entity model:
#Entity
public class User extends AbstractEntity implements UserDetails {
private static final long serialVersionUID = 5745401123028683585L;
public static final PasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();
#NotNull(message = "The name of the user cannot be blank")
#Column(nullable = false)
private String name;
/** CONTACT INFORMATION **/
private String landlinePhone;
private String mobilePhone;
#NotNull(message = "The username cannot be blank")
#Column(nullable = false, unique = true)
private String username;
#Email(message = "The email address is not valid")
private String email;
#JsonIgnore
private String password;
#Column(nullable = false)
private String timeZone = "Europe/Rome";
#JsonIgnore
private LocalDateTime lastPasswordResetDate;
#Column(nullable = false, columnDefinition = "BOOLEAN default true")
private boolean enabled = true;
#Type(type = "json")
#Column(columnDefinition = "json")
private Roles[] roles = new Roles[] {};
and my enum Roles is:
public enum Roles {
ROLE_ADMIN, ROLE_USER, ROLE_MANAGER, ROLE_TECH;
#JsonCreator
public static Roles create(String value) {
if (value == null) {
throw new IllegalArgumentException();
}
for (Roles v : values()) {
if (value.equals(v.toString())) {
return v;
}
}
throw new IllegalArgumentException();
}
}
I'm creating a client in Angular 4. Spring Data REST is great and expose repository easily return my model HATEOAS compliant:
{
"_embedded": {
"users": [
{
"name": "Administrator",
"username": "admin",
"roles": [
"Amministratore"
],
"activeWorkSession": "",
"_links": {
"self": {
"href": "http://localhost:8080/api/v1/users/1"
},
"user": {
"href": "http://localhost:8080/api/v1/users/1{?projection}",
"templated": true
}
}
},
Like you can see I'm also translating via rest-messages.properties the value of my enums. Great!
My Angular page now needs the complete lists of roles (enums). I've some question:
understand the better way for the server to return the list of roles
how to return this list
My first attemp was to create a RepositoryRestController in order to take advantage of what Spring Data REST offers.
#RepositoryRestController
#RequestMapping(path = "/api/v1")
public class UserController {
#Autowired
private EntityLinks entityLinks;
#RequestMapping(method = RequestMethod.GET, path = "/users/roles", produces = "application/json")
public Resource<Roles> findRoles() {
Resource<Roles> resource = new Resource<>(Roles.ROLE_ADMIN);
return resource;
}
Unfortunately, for some reason, the call to this methods return a 404 error. I debugged and the resource is created correctly, so I guess the problem is somewhere in the JSON conversion.
how to return this list?
#RepositoryRestController
#RequestMapping("/roles")
public class RoleController {
#GetMapping
public ResponseEntity<?> getAllRoles() {
List<Resource<Roles>> content = new ArrayList<>();
content.addAll(Arrays.asList(
new Resource<>(Roles.ROLE1 /*, Optional Links */),
new Resource<>(Roles.ROLE2 /*, Optional Links */)));
return ResponseEntity.ok(new Resources<>(content /*, Optional Links */));
}
}
I was playing around with this and have found a couple of ways to do it.
Assume you have a front end form that wants to display a combo box containing priorities for a single Todo such as High, Medium, Low. The form needs to know the primary key or id which is the enum value in this instance and the value should be the readable formatted value the combo box should display.
If you wish to customize the json response in 1 place only such as a single endpoint then I found this useful. The secret sauce is using the value object PriorityValue to allow you to rename the json field through #Relation.
public enum Priority {
HIGH("High"),
NORMAL("Normal"),
LOW("Low");
private final String description;
Priority(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public static List<Priority> orderedValues = new ArrayList<>();
static {
orderedValues.addAll(Arrays.asList(Priority.values()));
}
}
#RepositoryRestController
#RequestMapping(value="/")
public class PriorityController {
#Relation(collectionRelation = "priorities")
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
private class PriorityValue {
private String id;
private String value;
public PriorityValue(String id,
String value) {
this.id = id;
this.value = value;
}
}
#GetMapping(value = "/api/priorities", produces = MediaTypes.HAL_JSON_VALUE)
public ResponseEntity<Resources<PriorityValue>> getPriorities() {
List<PriorityValue> priorities = Priority.orderedValues.stream()
.map(p -> new PriorityValue(p.name(), p.getDescription()))
.collect(Collectors.toList());
Resources<PriorityValue> resources = new Resources<>(priorities);
resources.add(linkTo(methodOn(PriorityController.class).getPriorities()).withSelfRel());
return ResponseEntity.ok(resources);
}
}
Another approach is to use a custom JsonSerializer. The only issue using this is everywhere a Priority enum is serialized you will end up using this format which may not be what you want.
#JsonSerialize(using = PrioritySerializer.class)
#Relation(collectionRelation = "priorities")
public enum Priority {
HIGH("High"),
NORMAL("Normal"),
LOW("Low");
private final String description;
Priority(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public static List<Priority> orderedValues = new ArrayList<>();
static {
orderedValues.addAll(Arrays.asList(Priority.values()));
}
}
#RepositoryRestController
#RequestMapping(value="/api")
public class PriorityController {
#GetMapping(value = "/priorities", produces = MediaTypes.HAL_JSON_VALUE)
public ResponseEntity<Resources<Priority>> getPriorities() {
Resources<Priority> resources = new Resources<>(Priority.orderedValues);
resources.add(linkTo(methodOn(PriorityController.class).getPriorities()).withSelfRel());
return ResponseEntity.ok(resources);
}
}
public class PrioritySerializer extends JsonSerializer<Priority> {
#Override
public void serialize(Priority priority,
JsonGenerator generator,
SerializerProvider serializerProvider)
throws IOException, JsonProcessingException {
generator.writeStartObject();
generator.writeFieldName("id");
generator.writeString(priority.name());
generator.writeFieldName("value");
generator.writeString(priority.getDescription());
generator.writeEndObject();
}
}
The final json response from http://localhost:8080/api/priorities
{
"_embedded": {
"priorities": [
{
"id": "HIGH",
"value": "High"
},
{
"id": "NORMAL",
"value": "Normal"
},
{
"id": "LOW",
"value": "Low"
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/api/priorities"
}
}
}

Spring rest controller giving unsupported content type

Hello all here is what i have:
StockController.java
#RestController
public class StockController {
#Autowired
private StockRepository repository;
#RequestMapping(value = "stockmanagement/stock")
public ResponseEntity<?> addStock(#RequestBody String stock
) {
System.out.println(stock);
return new ResponseEntity<>(HttpStatus.OK);
}
when I make a request like so using chrome advanced rest extension :
Raw Headers
Content-Type: application/json
Raw Payload
{"stock": {"productId": 2, "expiryAndQuantity" : {}, "id": 0}}
It works fine in that out comes a string of json
However when i try to replace String stock with Stock stock where stock looks like this:
public class Stock {
#Id
private String id;
private String productId;
private Map<LocalDateTime, Integer> expiryAndQuantity;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public Map<LocalDateTime, Integer> getExpiryAndQuantity() {
return expiryAndQuantity;
}
public void setExpiryAndQuantity(Map<LocalDateTime, Integer> expiryAndQuantity) {
this.expiryAndQuantity = expiryAndQuantity;
}
#Override
public String toString() {
return String.format(
""
);
}
}
I get an error where by the following is fed back to me:
"status": 415
"error": "Unsupported Media Type"
"exception": "org.springframework.web.HttpMediaTypeNotSupportedException"
"message": "Content type 'application/json;charset=UTF-8' not supported"
"path": "/stockmanagement/stock"
My question is; how do i create a request which maps to my Stock object.
You can try with #JsonRootName annotation, by default Spring serialize using no root name value. like this:
{"productId": 2, "expiryAndQuantity" : {}, "id": 0}
But if you want that your serialization has a rootname you need to use #JsonRootName annotation.
#JsonRootName(value = "Stock")
And it'll produce something like this
{"Stock": {"productId": 2, "expiryAndQuantity" : {}, "id": 0}}
You can see more here
http://www.baeldung.com/jackson-annotations
instead of accepting a String Accept a Stock object.and accept it from a post request than having a get request
#RequestMapping(value = "stockmanagement/stock",method=RequestMethod.POST)
public ResponseEntity<?> addStock(#RequestBody Stock stock){
}
and your request should be sent like this
{
"productId": 2
,"expiryAndQuantity" : null
,"id": 0
}
all parameter names should be equal to the objects filed names,since spring has jackson binders on class path and object will be created inside the controller method. if you are planning on passing different parameters from the post request you can use
#JsonProperty("pid")
private String productId;
on the field name.

How to post to a URL with hash + range key Spring Data DynamoDB

As in spring-data-dynamoDB demo, I have created my application with hash and range keys, but am unable to post any data into my Table using POST because the following exception,
{cause: {cause: {cause: null,message: null}, message: "N/A (through reference chain: pkg.Test["id"])"}, message: "Could not read JSON: N/A (through reference chain: pkg.Test["id"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: N/A (through reference chain: pkg["id"])"
}
My Domain Class,
#DynamoDBTable(tableName = "test")
public class Test implements Serializable{
private static final long serialVersionUID = 1L;
#Id private TestId testId;
private String description;
private String testing;
#DynamoDBHashKey(attributeName="id")
public String getId() {
return testId != null ? testId.getId() : null;
}
public void setId(String id) {
if(testId == null){
testId = new TestId();
}
this.setId(id);
}
#DynamoDBRangeKey(attributeName="name")
public String getName() {
return testId != null ? testId.getName() : null;
}
public void setName(String name) {
if(testId == null){
testId = new TestId();
}
this.setName(name);
}
#DynamoDBAttribute(attributeName="description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#DynamoDBAttribute(attributeName="testing")
public String getTesting() {
return testing;
}
public void setTesting(String testing) {
this.testing = testing;
}
public TestId getTestId() {
return testId;
}
public void setTestId(TestId testId) {
this.testId = testId;
}
}
and my TestId Class,
public class TestId implements Serializable{
private String id;
private String name;
#DynamoDBHashKey
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#DynamoDBRangeKey
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I think I have created Domain class correctly but What is the correct procedure to Post data into it. I have tried,
URL:
http://localhost:8080/tests
Request Body:
{"testId": {"id": "test", "name": "z"}, "description": "Awesome Guy", "testing": "x"}
and
{"id": "test", "name": "z", "description": "Awesome Guy", "testing": "x"}
But all shows the exception as I mentioned above but I have given id attribute in requestbody correctly.
What is the correct procedure to POST the data into my table? and Is there anything problem with spring-data-rest parsing? as mentioned here
The setId() method seems to be self-calling. You may want to call testId.setId() instead of this.setId().

Resources