how to get response in json format - spring-boot

I am trying to fetch videos from student id and subject id . here i want to fetch only 3 columns from database with json format
Below is my code can anyone help me solve my issue.
Entity
public class Video extends AbstractEntity {
#Column(name = "video_name")
private String videoName;
private boolean active;
#Column(name = "media_location")
private String mediaLocation;
#Column(name = "media_type")
private String mediaType;
#Column(name = "class_id")
private Integer classId;
#Column(name = "subject_id")
private Integer subjectId;
}
Controller
#GetMapping("/video")
public ResponseEntity<?> getByClassidAndSubjectId(#RequestParam("class id") Integer classId,
#RequestParam("subject id") Integer subjectId) {
return service.getByClassidAndSubjectId(classId,subjectId);
}
Service
public ResponseEntity<?> getByClassidAndSubjectId(Integer classId, Integer subjectId) {
List<Video> videos = repository.getByClassIdAndSubjectId(classId, subjectId);
if (videos.isEmpty()) {
return new ResponseEntity<Object>("no videos found by classId " + classId + " AND subjectId " + subjectId,
HttpStatus.NOT_FOUND);
} else {
List<Object> video = repository.findByClassIdAndSubjectId(classId,subjectId);
return new ResponseEntity<Object>(video, HttpStatus.OK);
}
}
Repository
#Query(value = "SELECT id,video_name,media_location FROM video WHERE class_id=:classId AND subject_id=:subjectId", nativeQuery = true)
List<Object> findByClassIdAndSubjectId(#Param("classId")Integer classId,#Param("subjectId") Integer subjectId);
i am getting response like this
[
[
5,
"kannada",
"aws"
],
[
7,
"english",
"aws"
]
]
but i want response like
[
{
"id": 1,
"videoName": "xyz",
"videopath": "xyz"
},
{
"id": 2,
"videoName": "abc",
"videopath": "abc"
}
]

add json dependency your POM file https://mvnrepository.com/artifact/org.json/json
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20210307</version>
</dependency>
more details :- https://www.json.org/

Related

One to Many save (POST) results in Bad Request (400) using Fetch for List<Entity>

I am trying to save Parent (One) and Children (Many) entities at the same time.
I took help from here and here.
I have an User Entity like below:
#Entity
#Table(name = "app_user")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class AppUser {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "salutation")
private String salutation;
#Column(name = "name")
private String name;
#Column(name = "email")
private String email;
#Column(name = "preference")
private String preference;
public AppUser(String salutation, String name, String email, String preference, List<Address> addressList,
List<Expertise> expertise) {
super();
this.salutation = salutation;
this.name = name;
this.email = email;
this.preference = preference;
this.addressList = addressList;
this.expertise = expertise;
}
#OneToMany(orphanRemoval = true, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "address_id")
private List<Address> addressList = new ArrayList<>();
#OneToMany(orphanRemoval = true, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "expertise_id")
private List<Expertise> expertise = new ArrayList<>();
My POST controller method.
#PostMapping("/appUsers")
public ResponseEntity<AppUser> createUser(#RequestBody AppUser appUser) {
try {
AppUser _appUser = appUserRepository.save(
new AppUser(appUser.getSalutation(), appUser.getName(), appUser.getEmail(),
appUser.getPreference(), appUser.getAddressList(),
appUser.getExpertise()));
return new ResponseEntity<>(_appUser, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
My pure JS (Fetch) snippet:
<script>
async function postDataToServer(postData) {
const baseURL = "http://localhost:8080/api";
try {
const res = await fetch(`${baseURL}/appUsers`, {
method: "post",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(postData),
});
if (!res.ok) {
const message = `An error has occured: ${res.status} - ${res.statusText}`;
throw new Error(message);
}
} catch (err) {
alert(err.message);
}
}
</script>
Using above, I can see the form data nicely forming up like below:
{
"salutation": "Mr.",
"name": "Ajay Kumar",
"email": "ajay#kumar.com",
"address_main": "1234 StreetName State 12345",
"address_1": "2345 StreetName State 23456",
"address_2": "3456 StreetName State 34567",
"preference": "Vegeterian",
"expertise": [
"java",
"springboot",
"javascript"
],
"secret": "1abc1234-1abc-4321-1234-1234abcd1234"
}
During submit if I don't select expertise, it all works find. i.e. the user gets saved but if I select expertise checkboxes I get a 400 bad request message at the browser console and JSON parse erroSTS console like this:
2022-02-25 11:02:53.009 WARN 25007 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of com.spring.boot.rocks.model.Expertise (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('java'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of com.spring.boot.rocks.model.Expertise (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('java') at [Source: (PushbackInputStream); line: 1, column: 234] (through reference chain: com.spring.boot.rocks.model.AppUser["expertise"]->java.util.ArrayList[0])]
I created a github project here if any more details are required.
Question: What I am missing? How do I convert expertise collection to List using pure JS only ? Or how do I handle expertise collection in controller?
Your form data is not in correct format. This should be like this:
{
"salutation": "Mr.",
"name": "Ajay Kumar",
"email": "ajay#kumar.com",
"address_main": "1234 StreetName State 12345",
"address_1": "2345 StreetName State 23456",
"address_2": "3456 StreetName State 34567",
"preference": "Vegeterian",
"expertise": [
{
"java",
"springboot",
"javascript"
}
],
"secret": "1abc1234-1abc-4321-1234-1234abcd1234"
}
Expertise and address in your parent class are lists, not normal objectType entity. If any of these two lists are not present, try to set them as emptyList before saving.

Cannot construct instance of `javax.naming.Name` in jackson

We work on Spring Boot and oracle LDAP (Oid) with Spring LDAP module. The connection to LDAP with spring is ok but when we want to create LDAP group with ldapTemplate.create() method we got this error from Jackson:
please help us that what's wrong here!thanks
Type definition error: [simple type, class javax.naming.Name]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of javax.naming.Name (no Creators, like default construct, exist)
//Group Entry
#Getter
#Setter
#Entry(objectClasses = {"top", "groupOfUniqueNames"}, base = "cn=Groups")
public final class Group {
private static final String BASE_DN = "dc=eis,dc=msc,dc=ir";
#Id
private Name dn;
#Attribute(name="cn")
#DnAttribute("cn")
private String name;
#Attribute(name = "displayName")
private String description;
#Attribute(name = "owner")
private String owner;
#Attribute(name="uniqueMember")
private Set members;
public Group() {
}
public Group(String name, Set members) {
Name dn = LdapNameBuilder.newInstance(BASE_DN)
.add("ou", "groups")
.add("cn", name)
.build();
this.dn = dn;
this.name = name;
this.members = members;
}
public Group(Name dn, String name, String description, String owner, Set members) {
this.dn = dn;
this.name = name;
this.description = description;
this.owner = owner;
this.members = members;
}
public void addMember(Name member) {
if (this.members == null){
this.members = new HashSet<>();
}
members.add(member);
}
public void removeMember(Name member) {
members.remove(member);
}
#Override
public String toString() {
return "Group{" +
"dn=" + dn +
", name='" + name + '\'' +
", members=" + members +
'}';
}
//Service
#Override
public int createOIDGroupByJob(Group group) {
try {
ldapTemplate.create(group);
return 1;
}catch (Exception e){
return 0;
}
}
#PostMapping("/api/ldap/group")
public ResponseEntity<?> add(#RequestBody Group group) {
int retVal = ldapService.createOIDGroupByJob(group);
if (retVal==1) {
return new ResponseEntity<>(HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED);
}
}
//in postman
{
"dn": {
"rdns": [
{
"value": "Groups",
"type": "cn"
},
{
"value": "some data",
"type": "cn"
}
]
},
"fullName": "some data",
"lastName": "some data"
}
Finally!! I solved the problem!the problem is the jackson want to Deserialized an interface!! (javax.naming.Name) and it is not woking!
I found the solution is that use #JsonCreator on constructor like this:
//User Ldap Entry
public User(Name dn, String name, String lastName, String group) {
this.dn = dn;
this.name = name;
this.lastName = lastName;
this.group = group;
}
#JsonCreator
public User(#JsonProperty("dn") #JsonDeserialize(as=LdapName.class) final Name dn) {
this.dn = dn;
}
By the above sample, in #JsonDeserialize(as=LdapName.class) I pass LdapName.class that is the one of Name implemention! it works fine:)

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"
}
}
}

GSON,AndroidAnnotations - Expected BEGIN_OBJECT but was String

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?

Resources