I have written oauth2 social client but could not fetch authorized user's friends list
Please have a look at my code to see what's missing/
regards
please look at #RequestMapping("vkontakte/friends")
java 1.8 spring security
#SpringBootApplication
#RestController
#EnableOAuth2Client
public class SocialApplication extends WebSecurityConfigurerAdapter {
#Autowired
OAuth2ClientContext oAuth2ClientContext;
#RequestMapping({ "/user", "/me" })
public Map<String, String> user(Principal principal) {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", principal.getName());
return map;
}
//TODO как это оформить на фронтенде?
#RequestMapping("/vkontakte/friends")
public Map<String,String> friends() {
OAuth2RestTemplate vkTemplate = new OAuth2RestTemplate(vk(), oAuth2ClientContext);
UserInfoTokenServices tokenServicesvk = new UserInfoTokenServices(vkResource().getUserInfoUri(), vk().getClientId());
tokenServicesvk.setRestTemplate(vkTemplate);
ObjectNode resultNode = vkTemplate.getForObject(vkResource().getUserFriendsInfoUri(), ObjectNode.class);
ArrayNode data = (ArrayNode) resultNode.get("data");
Map<String, String> map = new LinkedHashMap<>();
for (JsonNode dataNode : data) {
//TODO надо как то правильно все получить?
}
return map;
In order to fetch friends from Vkontakte, you must declare a friend object, which will contain all the fields JSON structure of a friend has.
According to documentation, every friend has an id, first name, and last name, however, the response object is a little bit more complex than we need, so you might remove what you do not need.
Assuming we need all of the attributes of the response we can come up with two objects: result and friend.
Vkontakte friend object
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class VkontakteFriend {
private Long id;
#JsonProperty("first_name")
private String firstName;
#JsonProperty("last_name")
private String lastName;
}
Generic result object
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
#Data
#NoArgsConstructor
#AllArgsConstructor
public class FriendResponse {
private Long count;
private List<VkontakteFriend> items;
}
The only thing is left is to call API and get your response mapped to Java objects.
vkTemplate.getForObject(vkResource().getUserFriendsInfoUri(), FriendResponse.class);
Related
I have POJO class like this
class Data {
#JsonAlias({"username", "name"})
String surname;
Type type;
}
enum Type{
PERSON, USER
}
I want serialization of the Data class but when type is PERSON, JSON property surname is name and when type is USER, surname field as the name
Actually, I can create more child classes of Data class but my real type is more than 10 types with different names, and only difference is name of JSON property and do work similar.
Probably the simplest option would be to use com.fasterxml.jackson.annotation.JsonAnyGetter annotation. Create a method which returns Map<String, String> and create pair which meets your condition. Below code shows how it could look like:
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class DataApp {
private final static JsonMapper JSON_MAPPER = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.addModule(new JavaTimeModule())
.build();
public static void main(String[] args) throws Exception {
List<Role> roles = List.of(new Role("John", Type.USER), new Role("Tom", Type.PERSON));
JSON_MAPPER.writeValue(System.out, roles);
}
}
#Data
#NoArgsConstructor
#AllArgsConstructor
class Role {
#JsonIgnore
String name;
Type type;
#JsonAnyGetter
private Map<String, String> createDynamicProperties() {
if (Objects.isNull(type)) {
return Map.of();
}
return switch (type) {
case USER -> Map.of("name", name);
case PERSON -> Map.of("surname", name);
};
}
}
enum Type {
PERSON, USER
}
Above code prints:
[ {
"type" : "USER",
"name" : "John"
}, {
"type" : "PERSON",
"surname" : "Tom"
} ]
See also:
How to use dynamic property names for a Json object
Adding a dynamic json property as java pojo for jackson
I have the the following JSON and the following code I am getting object the object but how to fetch one by one and how to set in POJO class?it is basically google direction map api response i want to store only legs and waypoints
{
"legs":[
{"end_address":"Uttam Nagar, Delhi, 110059, India",
"end_location":{
"lat":28.6195607,
"lng":77.0550097
},
"start_address":"Delhi, India",
"start_location":{
"lat":28.7040873,
"lng":77.1024072
}
}
]}
#PostMapping("/create")
public ResponseEntity<ResponseWrapper> savedRoutedata(#RequestBody MapRequest data) throws JsonProcessingException {
ObjectMapper mapper=new ObjectMapper();
String jsonContent = mapper.writeValueAsString(data);
JsonObject o=new JsonParser().parse(jsonContent).getAsJsonObject();
System.out.println("My Legs:-"+o.get("legs"));
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
public class MapRequest {
#JsonProperty("legs")
private List<Legs> legs;
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonProperty("overview_path")
private List<WaypointPath>wayPoint;
}
In case your POJOs are correct, your MapRequest should already have everything set and you can use them as any Java object, without the need to convert them however.
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Controller
public class MyTestController {
#GetMapping("/create")
public String savedRoutedata(#RequestBody MapRequest data) {
for(Legs leg : data.getLegs()) {
System.out.println(leg);
}
return null;
}
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
class MapRequest {
#JsonProperty("legs")
private List<Legs> legs;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
class Legs {
#JsonProperty("end_address")
private String endAddress;
#JsonProperty("start_address")
private String startAddress;
#JsonProperty("end_location")
private Location endLocation;
#JsonProperty("start_location")
private Location startLocation;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
class Location {
#JsonProperty("lat")
private Double lat;
#JsonProperty("lng")
private Double lng;
}
I am trying to create a Map<String, Product> from my .properties file in Spring boot 3.1. I have written my custom converter for this, but it seems that it is never executing. I haven't done custom conversions before and I am not sure if a converter is needed in this use case or if Spring boot is able to manage the conversion automagically (If I configure it correctly).
product-configurations.properties:
products.product[0].name=FirstProduct
products.product[0].productID=1234
products.product[0].availableOptions=Opt1
products.product[0].processing=Parallel
Product class:
import lombok.Getter;
import lombok.Setter;
#Getter
#Setter
public class Product {
private String name;
private String processing;
private String availableOptions;
private Integer productID;
}
Configuration class:
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import lombok.Getter;
import lombok.Setter;
#Configuration
#PropertySource("classpath:product-configurations.properties")
#ConfigurationProperties("products")
#Getter
#Setter
public class ProductProperties {
private List<Product> product;
}
ProductConverter class:
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import com.cb.clientfacingapp.model.Product;
#Component
#ConfigurationPropertiesBinding
public class ProductConverter implements Converter<List<Product>, Map<String, Product>> {
#Override
public Map<String, Product> convert(List<Product> source) {
Product p = new Product();
System.out.println("I am converting");
return null;
}
}
Can someone pls guide me on this?
Thanks in Advance.
Update:
Sorry, I forgot to mention how the map should be organized.
Map<String, Product> where String is basically the name property of the product, and Product is the POJO class for that specific product, So each Product is mapped to its Name, basically.
Seems like what you're trying to achieve can be done without conversion
root.products.productName1.name=FirstProduct
root.products.productName1.productID=1234
root.products.productName1.availableOptions=Opt1
root.products.productName1.processing=Parallel
root.products.productName2.name=SecondProduct
root.products.productName2.productID=5678
root.products.productName2.availableOptions=Opt2
root.products.productName2.processing=Parallel
Therefore productName1, productName2 would be your map key names.
You can use it this way:
#ConfigurationProperties("root")
#Getter
#Setter
public class ProductProperties {
private Map<String, Product> products;
}
You could do what #Daria has suggested, which is the simplier way to do it.
In case you're still wondering how to convert a list into a Map, it can be done using Streams:
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import com.cb.clientfacingapp.model.Product;
#Component
#ConfigurationPropertiesBinding
public class ProductConverter implements Converter<List<Product>, Map<String, Product>> {
#Override
public Map<String, Product> convert(List<Product> source) {
return source.stream().collect(
Collectors.toMap(Product::getName,Function.identity));
}
}
It would generate a map with the product name as key value, and the product object as content value.
I have two classes. RequestDTO and Entity. I want to map RequestDTO to the Entity. In that case, I want to insert one of the Entity property manually which means that property is not in the Request DTO. How to achieve this using modelmapper.
public class RequestDTO {
private String priceType;
private String batchType;
}
public class Entity {
private long id;
private String priceType;
private String batchType;
}
Entity newEntity = modelMapper.map(requestDto, Entity.class);
But this does not work, it says it can't convert string to long. I request a solution to this or a better approach to this.
If you want to perform the mapping manually (ideally for dissimilar objects)
You can check the documentation for dissimilar object mapping Property Mapping,
You can define a property mapping by using method references to match
a source getter and destination setter.
typeMap.addMapping(Source::getFirstName, Destination::setName);
The source and destination types do not need to match.
typeMap.addMapping(Source::getAge, Destination::setAgeString);
If you don't want to do the mapping field by field to avoid boilerplate code
you can configure a skip mapper, to avoid mapping certain fields to your destination model:
modelMapper.addMappings(mapper -> mapper.skip(Entity::setId));
I've created a test for your case and the mapping works for both side without configuring anything :
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Before;
import org.junit.Test;
import org.modelmapper.ModelMapper;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
public class ModelMapperTest {
private ModelMapper modelMapper;
#Before
public void beforeTest() {
this.modelMapper = new ModelMapper();
}
#Test
public void fromSourceToDestination() {
Source source = new Source(1L, "Hello");
Destination destination = modelMapper.map(source, Destination.class);
assertNotNull(destination);
assertEquals("Hello", destination.getName());
}
#Test
public void fromDestinationToSource() {
Destination destination = new Destination("olleH");
Source source = modelMapper.map(destination, Source.class);
assertNotNull(source);
assertEquals("olleH", destination.getName());
}
}
#Data
#NoArgsConstructor
#AllArgsConstructor
class Source {
private Long id;
private String name;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
class Destination {
private String name;
}
I can't make spring serialize the response when results is array/list .
So when I call clients from RestController it does return [{},{},{}], instead of real objects, all other methods works just fine.
package com.test.Domain.Client;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.UUID;
#Entity
#Table(name = "client")
public class Client {
#Column(name = "client_id")
#Id
private UUID clientId;
#Column(name = "name")
private String name;
private Client() {
}
private Client(UUID clientId, String name) {
this.clientId = clientId;
this.name = name;
}
public static Client create(String name)
{
return new Client(UUID.randomUUID(), name);
}
}
package com.test.Rest;
import com.test.Domain.Calendar.AppointmentRepository;
import com.test.Domain.Client.Client;
import com.test.Domain.Client.ClientRepository;
import com.test.Domain.Worker.WorkerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
#org.springframework.web.bind.annotation.RestController
public class RestController {
#Autowired
private ClientRepository clientRepository;
#Autowired
private WorkerRepository workerRepository;
#Autowired
private AppointmentRepository appointmentRepository;
#RequestMapping(path = "/client", method = RequestMethod.POST)
public void registerClient(#RequestParam(name = "name") String name) {
this.clientRepository.save(Client.create(name));
}
#RequestMapping(path = "/clientCount", method = RequestMethod.GET)
public Long countClient() {
return this.clientRepository.count();
}
#RequestMapping(path = "/clients", method = RequestMethod.GET)
#ResponseBody
public List<Client> clients() {
List<Client> list = new ArrayList<Client>();
for (Client client : this.clientRepository.findAll()) {
list.add(client);
}
return list;
}
}
Jackson needs Getter and Setter methods in order to serialize the Client object properly into JSON. Therefore a list of empty objects is returned and the values for the members are missing. Add them to Client and the response should look fine.
Spring applies first registered applicable by response mime-type HttpMessageConverter implementation when serializing the response to /clients call. In your case this is some JSON serializer. As you have no JSON configuration specified on Client class the default POJO serializing approach is used: reflection scanning of object properties. As mentioned earlier your Client class doesn't define any properties (at least getters), so serializer do not detect any.
Please refer to the following article for a more detailed explanation: https://www.javacodegeeks.com/2013/07/spring-mvc-requestbody-and-responsebody-demystified.html
P.S. Marking method with #ResponseBody in #RestController annotated class is not necessary as itself is a convenience annotation aggregating #Controller and #ResponseBody.