I want my string method to return value. The below code returns a view(jsp). In the code "line" stores the product details.I want the method to return this value. Could someone show me how to do it.
#RequestMapping(value="addtocart{id}")
#ResponseBody
public String addToCart(#PathVariable("id") int id, #ModelAttribute("cart") Cart cart)
{
Product product = productService.getProductById(id);
if (product != null) {
CartLine line = new CartLine();
line.setProduct(product);
line.setQuantity(1);
return "viewcart";
}
xml config
<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="prefixJson" value="false"/>
<property name="supportedMediaTypes" value="application/json"/>
</bean>
Just change the return type of the controller method to Product and return the Product instance.
Dependending on what HttpMessageConverters you have configured, the response will be the object serialized to JSON for example.
Update
You can see how to configure the Jackson converter here or in Spring MVC documentation.
Related
I have an object returned from item processor.
public class PcdRateMapper
{
private Pcdrate pcdRate;
private Boolean isValidPcdRate;
public PcdRateMapper ()
{
// pcdRate = new Pcdrate ();
}
public Pcdrate getPcdRate ()
{
return pcdRate;
}
public void setPcdRate (Pcdrate pcdRate)
{
this.pcdRate = pcdRate;
}
public Boolean getIsValidPcdRate ()
{
return isValidPcdRate;
}
public void setIsValidPcdRate (Boolean isValidPcdRate)
{
this.isValidPcdRate = isValidPcdRate;
}
Now i want to extract only Pcdrate object values in my item writer. How can I do this. Currently I'm using following spring configuration but getting invalid property exception. Thanks in advance.
<
property name="lineAggregator">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value="," />
<property name=""></property>
<property name="fieldExtractor">
<bean
class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name=""></property>
<property name="names"
value="company, subcoy" />
</bean>
</property>
</bean>
</property>
The invalid property exception may stem from
<property name=""></property>
where the property name is an empty string. You have that twice in the code above, remove it.
Your xml structure seems to be invalid, see spring_bean_definition
to see how it should look like.
On the bean of type BeanWrapperFieldExtractor you must set the property 'names' to the names of properties that you want to extraxt, in your case 'pcdRate'.
It should be configured like this :
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="pcdRate" />
</bean>
OK, so if I need to put some primitive values in the constructor, how do I do that?
#Autowired
public CustomBean(String name, #Qualifier("SuperBean") SuperBean superBean) {
super();
this.superBean = superBean;
this.name = name;
}
For instance here I am defining that the superBean has the Qualifier "SuperBean", but I'd also like to know how is it possible to use annotations to set the name value here?
I know it's possible with xml configuration, but I want to know how to do this with annotations too:
<bean id="CustomXmlBean" class="org.arturas.summerfav.beans.CustomXmlBean">
<constructor-arg name="name" type="String" value="The Big Custom XML Bean" />
<constructor-arg>
<bean id="SuperBean" class="org.arturas.summerfav.beans.SuperBean" />
</constructor-arg>
</bean>
Well how do I put in values for String, int and other generic types?
Here is one way to do this:
#Component
public class YourBean {
#Autowired
public YourBean(#Value("${prop1}") String arg1, #Value("${prop2}") String arg2) {
// rest of the code
}
}
I'm running into a problem where my JSON response can be object or an array of objects
Foobar example with a single value:
{
"foo": {"msg": "Hello World" }
}
Foobar example with an array:
{
"foo": [
{ "msg": "Hello World" },
{ "msg": "Goodbye World" }
]
}
I want the force the single value into any array but so far, the only way I found converted all single values as arrays.
ACCEPT_SINGLE_VALUE_AS_ARRAY
http://wiki.fasterxml.com/JacksonFeaturesDeserialization
I've been looking around for an annotation that does the same thing for a single property but so far google hasn't turned up any examples.
Has anyone run into this problem before, I really don't want to rewrite everything as arrays to make RestTemplate work with a buggy service.
I want the force the single value into any array but so far, the only
way I found converted all single values as arrays.
This simply shouldn't be the case. The ACCEPT_SINGLE_VALUE_AS_ARRAY property is on/off for a given ObjectMapper, but its behavior is entirely governed by the target property the JSON value is being mapped to.
When ACCEPT_SINGLE_VALUE_AS_ARRAY is on, mapping a JSON value to a Java collection property will not result in an error.
When ACCEPT_SINGLE_VALUE_AS_ARRAY is on, mapping a JSON value to a Java basic property will (also) not result in an error.
Illustrated by the following code:
class Foo {
private String msg;
// Constructor, setters, getters
}
class Holder {
private List<Foo> foo;
private Foo other;
// Constructors, setters, getters
}
public class FooTest {
#org.junit.Test
public void testCollectionFromJSONValue() throws Exception {
final InputStream stream = Thread.currentThread()
.getContextClassLoader().getResourceAsStream("foo.json");
final String json = IOUtils.toString(stream);
final ObjectMapper mapper = new ObjectMapper();
mapper.configure(
DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY,
true);
final Holder holder = mapper.readValue(json, Holder.class);
System.out.println(holder);
}
}
Which relies on the following JSON:
{
"foo": {
"msg": "Hello World"
},
"other": {
"msg": "Goodbye"
}
}
Running the code will show that the "foo" property is successfully deserialized into a list, whereas the "other" property gets deserialized into a (basic) Foo type.
I had the same issue and struggled finding a solution to generally configure my RestTemplate that way. Because you don't always want to instantiate and alter an objectMapper... So here's my solution:
<bean id="myRestTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</list>
</property>
</bean>
<bean id="jacksonObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper" />
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="jacksonObjectMapper" />
<property name="targetMethod" value="configure" />
<property name="arguments">
<list>
<value type="com.fasterxml.jackson.databind.DeserializationFeature">ACCEPT_SINGLE_VALUE_AS_ARRAY</value>
<value>true</value>
</list>
</property>
</bean>
You can then use this pre-configured RestTemplate by injecting it into your code:
#Autowired
private RestTemplate myRestTemplate;
Best Way to resolve this when using RestTemplate.
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, false);
MappingJackson2HttpMessageConverter jacksonMappingConverter
= new MappingJackson2HttpMessageConverter(objectMapper);
restTemplate.getMessageConverters().add(0, jacksonMappingConverter);
For the element to be parsed use the annotation which can be object or array define as below
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
public class ParentObject{
#JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
#JsonProperty("InnerObject")
private List<InnerObject> innerObject;
}
If you don't want to add new mapper to restTemplate , change the exisitng one to support the use case
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
for (HttpMessageConverter<?> httpMessageConverter : messageConverters) {
if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = (MappingJackson2HttpMessageConverter) httpMessageConverter;
mappingJackson2HttpMessageConverter.getObjectMapper()
.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
}
}
i just wonder, can i convert uri string to another object type ?
#RequestMapping(value="/key/{keyDomain}", method=RequestMethod.GET)
public String propertyEditor(#PathVariable(value="keyDomain") KeyDomain key, Model model){
model.addAttribute("key", key);
return "propertyEditor";
}
and here my configuration
<beans:bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<beans:property name="customEditors">
<beans:map>
<!-- <beans:entry key="com.template.baseline.domain.KeyDomain" value="com.template.baseline.propertyEditor.KeyPropertyEditor"/> -->
<beans:entry key="com.template.baseline.domain.KeyDomain">
<beans:ref bean="keyDomainPropertyEditor" />
</beans:entry>
</beans:map>
</beans:property>
</beans:bean>
<!-- key domain property editor bean -->
<beans:bean id="keyDomainPropertyEditor" class="com.template.baseline.propertyEditor.KeyPropertyEditor">
<beans:property name="keyDomain">
<beans:bean class="com.template.baseline.domain.KeyDomain" />
</beans:property>
</beans:bean>
and propertyEditor Class :
public class KeyPropertyEditor extends PropertyEditorSupport{
private KeyDomain keyDomain;
/**
* example : 10435
* - 10 will be keyId
* - 435 will be baseOfficeId
*/
#Override
public void setAsText(String text) throws IllegalArgumentException{
KeyDomain keyDomain = new KeyDomain();
keyDomain.setKeyId(Integer.parseInt(text.substring(0,1)));
keyDomain.setBaseOfficeId(Integer.parseInt(text.substring(2,4)));
setValue(keyDomain);
}
#Override
public String getAsText() {
KeyDomain value = (KeyDomain) getValue();
return (value != null ? value.toString() : "");
}
public void setKeyDomain(KeyDomain keyDomain) {
this.keyDomain = keyDomain;
}
}
i thought that i can use Property Editor to convert my URI string become appropriate object type. i already made an implementation and configure CustomEditorConfigurer, but i always get ConversionNotSupportedException.
if i add initBinder at my controller, everything will just fine :
#InitBinder
public void setBinder(WebDataBinder dataBinder) {
dataBinder.registerCustomEditor(KeyDomain.class, new KeyPropertyEditor());
}
and i get Warning something like this
WARN : org.springframework.beans.factory.config.CustomEditorConfigurer - Passing PropertyEditor instances into CustomEditorConfigurer is deprecated: use PropertyEditorRegistrars or PropertyEditor class names instead. Offending key [com.template.baseline.domain.KeyDomain; offending editor instance: com.template.baseline.propertyEditor.KeyPropertyEditor#1a271f5
thanks for the answer.
ps : webBindingInitalizer injected on AnnotationMethodHandlerAdapter
<beans:bean id="AnnotationMethodHandlerAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<beans:property name="webBindingInitializer">
<beans:bean class="com.template.baseline.initialize.CustomWebBindingInitializer" />
</beans:property>
</beans:bean>
and Implementation
public class CustomWebBindingInitializer implements WebBindingInitializer {
public CustomWebBindingInitializer(){
System.out.println("******** constructor *********");
}
public void initBinder(WebDataBinder binder, WebRequest request) {
System.out.println("******** initBinder *********");
binder.registerCustomEditor(KeyDomain.class, new KeyDomainPropertyEditor());
}
}
CustomEditorConfigurer has nothing to do with web request data binding.
If you want to register your PropertyEditor globablly, you need to implement WebBindingInitializer and supply AnnotationMethodHandlerAdapter with it:
<bean
class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<proeprty name = "webBindingInitializer">
<bean class = "MyWebBindingInitializer" />
</property>
</bean>
Another option is to implement your conversion logic as a Formatter and configure it via FormattingConversionServiceFactoryBean and <mvc:annotation-driven>, see mvc-showcase sample.
I'd like to convert this SimpleFormController to use the annotation support introduced in Spring MVC 2.5
Java
public class PriceIncreaseFormController extends SimpleFormController {
ProductManager productManager = new ProductManager();
#Override
public ModelAndView onSubmit(Object command)
throws ServletException {
int increase = ((PriceIncrease) command).getPercentage();
productManager.increasePrice(increase);
return new ModelAndView(new RedirectView(getSuccessView()));
}
#Override
protected Object formBackingObject(HttpServletRequest request)
throws ServletException {
PriceIncrease priceIncrease = new PriceIncrease();
priceIncrease.setPercentage(20);
return priceIncrease;
}
}
Spring Config
<!-- Include basic annotation support -->
<context:annotation-config/>
<!-- Comma-separated list of packages to search for annotated controllers. Append '.*' to search all sub-packages -->
<context:component-scan base-package="springapp.web"/>
<!-- Enables use of annotations on controller methods to map URLs to methods and request params to method arguments -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean name="/priceincrease.htm" class="springapp.web.PriceIncreaseFormController">
<property name="sessionForm" value="true"/>
<property name="commandName" value="priceIncrease"/>
<property name="commandClass" value="springapp.service.PriceIncrease"/>
<property name="validator">
<bean class="springapp.service.PriceIncreaseValidator"/>
</property>
<property name="formView" value="priceincrease"/>
<property name="successView" value="hello.htm"/>
<property name="productManager" ref="productManager"/>
</bean>
Basically, I'd like to replace all the XML configuration for the /priceincrease.htm bean with annotations within the Java class. Is this possible, and if so, what are the corresponding annotations that I should use?
Thanks,
Don
It'll look something like the following, although whether it works or not exactly as is will depend a bit on your configuration (view resolver, etc). I should also note that there are about eight billion valid ways to write this thing. See the Spring documentation, 13.11.4 "Supported handler method arguments and return types" for an overview of the insanity. Also note that you can autowire the validator
#Controller
#RequestMapping("/priceincrease.htm")
public class PriceIncreaseFormController {
ProductManager productManager;
#Autowired
public PriceIncreaseFormController(ProductManager productManager) {
this.productManager = productManager;
}
// note: this method does not have to be called onSubmit
#RequestMapping(method = RequestMethod.POST)
public String onSubmit(#ModelAttribute("priceIncrease") PriceIncrease priceIncrease, BindingResult result, SessionStatus status {
new PriceIncreaseValidator().validate(priceIncrease, result);
if (result.hasErrors()) {
return "priceincrease";
}
else {
int increase = priceIncrease.getPercentage();
productManager.increasePrice(increase);
status.setComplete();
return "redirect:hello.htm";
}
}
// note: this method does not have to be called setupForm
#RequestMapping(method = RequestMethod.GET)
public String setupForm(Model model) {
PriceIncrease priceIncrease = new PriceIncrease();
priceIncrease.setPercentage(20);
model.addAttribute("priceIncrease", priceIncrease);
return "priceincrease";
}
}
Someone completed this project with a recent MVC and it's on github, so you can see how all the classes are changed compared to Spring's tutorial.
Link: PriceIncreaseFormController