spring mvc json array double quotes converted to " - json

This is my controller.
#RequestMapping(value="/updateItem.htm", method = RequestMethod.GET, headers="Accept=*/*", produces = "application/json; charset=utf-8")
public #ResponseBody ModelAndView updateItem(ModelAndView model) {
List<String> list = aspireDAO.getSingleCol("select prod_id from items order by prod_id asc");
JsonArray jArray = AspireUtil.toJsonArray(list);
model.setViewName("UpdateItem");
model.addObject("items", jArray);
System.out.println(jArray);
return model;
}
for jackson support in spring-servlet.xml
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="html" value="text/html"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
<property name="prefixJson" value="true"/>
</bean>
</list>
</property>
</bean>
the json array is good in my controller sysout statement. When i check source of the page the json array is printed like this
'["amgc","atup","ciwy","eyqe","ijm
What could be the possible reason. How to fix it.
Regards
Aadam

To show the human readable values you should use
<c:out value="${items}" escapeXml="false"/>
or just
${items}
The reason why you're seeing this in the first place is that the special chars are encoded as html entities (the stuff you see) which are considered HTML safe, before being added to the output stream. On the other side, all the tags that are used for outputting the value, have a suitable escape attribute that gives the control if the values should be displayed escaped or not. JSTL display has escapeXml set to true by default, tweaking this will fix your issue

Related

Export Database Table To JSon Using Apache Camel Not Behaving As Expected

I would like to export a table of a database to JSON file with this code using the code listed below, the code runs but splits returned data rows into a file per record not a single file as expected.
Camel's route :
public void configure() throws Exception {
JsonDataFormat jsonFormat = new JsonDataFormat(JsonLibrary.XStream);
jsonFormat.setUnmarshalType(Customer.class);
from("sql: SELECT * FROM assignment01.staff?dataSourceRef=dataSource")
.marshal(jsonFormat)
.to("file:data/test");
}
there is my xml
<bean id="route" class="com.huyqtran.JSonRoute"/>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<routeBuilder ref="route"/>
</camelContext>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/assignment01?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</bean>
I expect that the application just create one JSON file for the whole table.
Tell Camel to append to the file with option fileExist=Append: http://camel.apache.org/file2, eg
.to("file:data/test?fileExist=Append");
or tell the SQL component to return the entire result-set by turning of its iterator, eg useIterator=false
from("sql: SELECT * FROM assignment01.staff?dataSourceRef=dataSource&useIterator=false")

Deserializing single-attribute JSON payload in Spring MVC controller

I want to create controller methods that semantically look like the following
public HttpEntity<?> deleteUser(String userId){
...
}
The client is going to pass the user ID as part of the JSON payload. If I try to annotate #RequestBody the string parameter and issue a {"userId":"foo"} payload, then I get an exception
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: java.io.PushbackInputStream#7311a203; line: 1, column: 1]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148) ~[jackson-databind-2.6.1.jar:2.6.1]
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:854) ~[jackson-databind-2.6.1.jar:2.6.1]
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:62) ~[jackson-databind-2.6.1.jar:2.6.1]
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11) ~[jackson-databind-2.6.1.jar:2.6.1]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3702) ~[jackson-databind-2.6.1.jar:2.6.1]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2798) ~[jackson-databind-2.6.1.jar:2.6.1]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:221) ~[spring-web-4.2.0.RELEASE.jar:4.2.0.RELEASE]
And that is reasonable because JSON wants to deserialize a complex object (with namely one attribute) into a String.
I also know that "foo" is not valid JSON. And I know that I can use a Map<String,Object> or even better a ModelMap, and as a last resort I could use query string and #RequestParam, but today I have been clearly asked by my boss to find a way to use a plain string instead of an object, in order for code to look more readable.
How can I force Jackson/MVC to deserialize only the "username" property into a plain old String?
You will usually see this type of error when Spring MVC finds a request mapping that matches the URL path but the parameters (or headers or something) don't match what the handler method is expecting.
If you use the #RequestBody annotation then Spring MVC is expecting to map the entire body of the POST request to an Object,it dont work with String by default.
There are different way to do this as listed below:
1) Change method type of deleteUser() method type to GET instead of Post and use userId as String.
2) You could simply inject the HttpServletRequest into your method and read the body:
public void deleteUser(HttpServletRequest request) {
String userID = IOUtils.toString( request.getInputStream());
// do stuff
}
3) Use a wrapper (java model of the JSON object) that could replace the String parameter,and also this will work fine with the json coming in your post.
public class UserWrapper {
private String userId;
//getter setters
and then use in your controller as:
public void deleteUser(#RequestBody UserWrapper user) {
//do your stuff
}
4) Spring provides a way to configure multiple message converters as shown below:
Note: Then, requests to the various methods must specify the "content-type" header with an appropriate value. For those methods where the request body is mapped to a JAXB bean, specify "application/xml". And for those where the request body is a String, use "text/plain".
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
<ref bean="marshallingConverter" />
<ref bean="stringHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="jsonConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
</bean>
<bean id="marshallingConverter"
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<constructor-arg ref="jaxb2Marshaller" />
<property name="supportedMediaTypes" value="application/xml"/>
</bean>
<bean id="stringHttpMessageConverter"
class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/plain"/>
</bean>
Hope this help you!

spring-mvc return raw json string

I want the convenience of automatically serializing objects into JSON and ability to return raw JSON string. I am using Gson instead of Jackson, since Gson has been in my app for a while and I have existing tweaks, converters, and annotations peppered throughout my app.
<mvc:annotation-driven >
<mvc:message-converters register-defaults="true">
<bean class="com.test.GSONHttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
I can automatically serialize pojo's:
#RequestMapping(value="foo/{name}", method = RequestMethod.GET)
public #ResponseBody Shop getShopInJSON(#PathVariable String name) {
return new Shop();
}
I want this to work also:
#RequestMapping(value="rawJsonTest/{name}", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody String rawJsonTest(#PathVariable String name) {
return "{\"test\":5}";
}
Result right now is an escaped value:
"{\"test\":5}"
instead of:
{"test":5}
The problem is that your custom converter takes precedence over the default ones. It's thus called, considers the String as a raw String that must be converted to JSON, and thus escapes the double quotes.
I'm not sure if and how it's possible with XML to register a converter after (and not before) the default ones, but you could set register-defaults to false and provide an explicit list of all the converters you want to apply. If org.springframework.http.converter.StringHttpMessageConverter is registered before your custom one, it will be called first and will send the returned String as is.
Thanks for the correct answer, #JB Nizet
Order matters:
<mvc:annotation-driven >
<mvc:message-converters register-defaults="true">
<bean class = "org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json; charset=UTF-8" />
</bean>
<bean class="com.test.GSONHttpMessageConverter" />
</mvc:message-converters>

Spring mvc configuration JSON

I would know how works the configuration about Spring MVC rest services that returns JSON.
I have configurated the applicationContenxt.xml in this way:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="contentNegotiatingViewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
</list>
</property>
</bean>
<bean class="com.MyController"></bean>
And this is the code of my controller:
#Controller(value="MyController")
public class MyController {
#RequestMapping(value="/getValue", method=RequestMethod.GET)
public ModelAndView getValue() {
Map model = new HashMap();
model.put("asasa", "bbbbb");
model.put("cccc", "ddddd");
return new ModelAndView("jsonView",model);
}
}
I'm missing something about xml configuration or Java code? I have always error 404 while trying to invoke this resource: http://localhost:8080/fss/MyController/getValue
Just do:
#Controller
public class HelloController {
#RequestMapping(value="/hello", method=RequestMethod.GET)
public #ResponseBody String hello(#RequestParam String name) {
return "Hi " + name;
}
}
Change the return type to an object and include jackson in the classpath for an object response.
The request need to have a application/json header for the controller to return json.
Check out http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/
And don't forget to add jackson converter to Spring context file.
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</mvc:message-converters>
</mvc:annotation-driven>
By the way - if your method accepts JSON, then use #RequestBody annotation with incoming data type:
#RequestMapping
public #ResponseBody OutgoingClass getJsonByJson(#RequestBody IncomingClass data) {...}
You can find nice examples of JSON and Spring MVC and more https://sites.google.com/site/upida4j/example

Getting more attributes from CAS than just user id

I am using CAS with JDBC Authentication handler and was wondering is it possible to get the other attributes of principal object (for e.g. firstname, lastname) not just the username from CAS after successful authentication?
In the casServiceValidationSuccess.jsp, I add like below:
<cas:attributes>
<c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}">
**<cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>**
</c:forEach>
</cas:attributes>
In the deployerConfigContent.xml, I add like below:
<bean class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" >
**<property name="attributeRepository">
<ref bean="attributeRepository" />
</property>**
</bean>
<bean id="attributeRepository" class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao">
<constructor-arg index="0" ref="dataSource"/>
<constructor-arg index="1" value="select * from bbs_members where {0}" />
<property name="queryAttributeMapping">
<map>
<entry key="username" value="username" />
</map>
</property>
<property name="resultAttributeMapping">
<map>
<entry key="uid" value="uid"/>
<entry key="email" value="email"/>
<entry key="password" value="password"/>
</map>
</property>
</bean>
It works.
I came across this problem during the debug, please close the browser if you change this JSP or XML files, otherwise the changes won't work. Be careful.
To get any user attributes from DB I did the following:
use PersonDirectoryPrincipalResolver
in deployerConfigContext.xml:
<bean id="primaryPrincipalResolver"
class="org.jasig.cas.authentication.principal.PersonDirectoryPrincipalResolver" >
<property name="attributeRepository" ref="singleRowJdbcPersonMultiplyAttributeDao" />
</bean>
instead of using standard SingleRowJdbcPersonAttributeDao class create your own implementation which returns not only one row from a query result but aggregated data from all returned rows:
copy all code from SingleRowJdbcPersonAttributeDao and change only one method parseAttributeMapFromResults.
you will have something like that:
public class SingleRowJdbcPersonMultiplyAttributeDao extends AbstractJdbcPersonAttributeDao<Map<String, Object>> {
...
#Override
protected List<IPersonAttributes> parseAttributeMapFromResults(final List<Map<String, Object>> queryResults, final String queryUserName) {
final List<IPersonAttributes> peopleAttributes = new ArrayList<IPersonAttributes>(queryResults.size());
Map<String, List<Object>> attributes = new HashMap<String, List<Object>>();
for (final Map<String, Object> queryResult : queryResults) {
for (final Map.Entry<String, Object> seedEntry : queryResult.entrySet()) {
final String seedName = seedEntry.getKey();
final Object seedValue = seedEntry.getValue();
if (attributes.get(seedName) != null && !attributes.get(seedName).get(0).equals(seedValue)) {
attributes.get(seedName).add(seedValue);
} else {
List<Object> list = new ArrayList<Object>();
list.add(seedValue);
attributes.put(seedName, list);
}
}
}
final IPersonAttributes person;
final String userNameAttribute = this.getConfiguredUserNameAttribute();
if (this.isUserNameAttributeConfigured() && attributes.containsKey(userNameAttribute)) {
// Option #1: An attribute is named explicitly in the config,
// and that attribute is present in the results from LDAP; use it
person = new CaseInsensitiveAttributeNamedPersonImpl(userNameAttribute, attributes);
} else if (queryUserName != null) {
// Option #2: Use the userName attribute provided in the query
// parameters. (NB: I'm not entirely sure this choice is
// preferable to Option #3. Keeping it because it most closely
// matches the legacy behavior there the new option -- Option #1
// -- doesn't apply. ~drewwills)
person = new CaseInsensitiveNamedPersonImpl(queryUserName, attributes);
} else {
// Option #3: Create the IPersonAttributes doing a best-guess
// at a userName attribute
person = new CaseInsensitiveAttributeNamedPersonImpl(userNameAttribute, attributes);
}
peopleAttributes.add(person);
return peopleAttributes;
}
...
}
and in deployerConfigContext.xml:
<bean id="singleRowJdbcPersonMultiplyAttributeDao"
class="com.scentbird.SingleRowJdbcPersonMultiplyAttributeDao">
<constructor-arg index="0" ref="dataSource" />
<constructor-arg index="1" value="SELECT attributes_table1.*, attributes_table2.attr1, attributes_table2.roles AS roles FROM user_table ut LEFT JOIN roles_table rt ON <condition> LEFT JOIN another_table at ON <condition> WHERE {0}" />
<property name="queryAttributeMapping">
<map>
<entry key="username" value="username" />
</map>
</property>
</bean>
Also in my case I used SAML protocol.
As a result you will get on the client all attributes which your select returns.
For example, if user have many roles you could have on the client:
User: username, firstname, lastname, email, ... , [ROLE_1, ROLE_2, ROLE_3]
My case works with Spring Security and Grails.
I'm not sure this is 100% Feng Shui solution :) as it's fast cooked but it works in our case.
Hope it helps.
I just spent the last three days attempting to get CAS properly configured. One of the issues I encountered was that I had to explicitly instruct CAS to publish the properties. I did this by:
opening https://localhost/cas/services
going to the 'Manage Services' tab
click 'edit' for each service
highlight the properties you wish to publish
click the save button
FWIW, the other issue is that casServiceValidationSuccess.jsp does contain any code to pass the properties back in the response. I was looking for a solution to this when I found your question. I notice that you have rewritten your implementation.
The definitive and complete solution is the following (for this undocumented feature):
Server side:
a. Add an attributeRepository to your CredentialsToPrincipalResolver.
b. Implement the your.package.YourPersonAttributeDao like an IPersonAttributeDao.
c. Declare the attributes that will be transmitted into assertion to client.
d. Modify the casServiceValidationSuccess.jsp to display the attributes (thx to xiongjiabin).
Client side. You get all attributes by doing this:
Due to formatting problem I can't post the code of the definitive solution.... Let me know if you are interested, I will send you an email with all the code.
In addition to the answer provided by #xiongjiabin if you are using CAS v4+ you probably want to use assertion.primaryAuthentication instead of assertion.chainedAuthentications in casServiceValidationSuccess.jsp:
<cas:attributes>
<c:forEach var="attr" items="${assertion.primaryAuthentication.principal.attributes}">
<cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>**
</c:forEach>
</cas:attributes>
If you do use assertion.chainedAuthentications with CAS v4+ then the serviceRegistryDao list of allowedAttributes will be ignored and all attributes will be returned.