Using JAXB to create spring bean and necessary properties - java

I am having trouble customizing my JAXB Marshaller. I have my marshaller code:
public void marshaller(AddressMap addMap, File file) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(AddressMap.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(addMap, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
The output looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ObjectMap>
<Prop> Indiana</Prop>
<Prop1>39.0</Prop1>
<Prop2>-85.0</Prop2>
<Prop3> United States</Prop3>
<Prop4> Hueseman Rd</Prop4>
<Prop5> 8540-8704</Prop5>
<Prop6> 47001</Prop6>
</ObjectMap>
Instead, I need it to look like this:
<bean class="classname">
<property name="PropName" value="object value" />
<property name="PropName1" value="object value" />
<property name="PropName2" value="object value" />
<property name="PropName3" value="object value" />
<property name="PropName4" value="object value" />
<property name="PropName5" value="object value" />
<property name="PropName6" value="object value" />
</bean>

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You could use MOXy's #XmlDesciminatorNode and #XmlPath extensions to map this use case. Below is an example based on what I assume your object model looks like.
ObjectMap
The #XmlDescriminatorNode annotation allows you to specify that you want a specific XML attribute to serve as the inheritance indicator.
package forum13884782;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;
#XmlRootElement(name="bean")
#XmlDiscriminatorNode("#class")
public class ObjectMap {
}
AddressMap
The #XmlDescriminatorValue annotation is used to specify the value on the descriminator node that relates to the instance class. In this class we also use the #XmlPath annotation to indicate which property element we wish to map to based on the value of its name attribute.
package forum13884782;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
#XmlRootElement(name="bean")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlDiscriminatorValue("AddressMap")
public class AddressMap extends ObjectMap {
#XmlPath("property[#name='PropName']/#value")
String prop;
#XmlPath("property[#name='PropName1']/#value")
String prop1;
#XmlPath("property[#name='PropName2']/#value")
String prop2;
#XmlPath("property[#name='PropName3']/#value")
String prop3;
#XmlPath("property[#name='PropName4']/#value")
String prop4;
#XmlPath("property[#name='PropName5']/#value")
String prop5;
#XmlPath("property[#name='PropName6']/#value")
String prop6;
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry.
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
The following demo code will convert the XML message to an instance of AddressMap and then back to XML.
package forum13884782;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(AddressMap.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum13884782/input.xml");
AddressMap addressMap = (AddressMap) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(addressMap, System.out);
}
}
input.xml/Output
<bean class="AddressMap">
<property name="PropName" value="Indiana" />
<property name="PropName1" value="39.0" />
<property name="PropName2" value="-85.0" />
<property name="PropName3" value="United States" />
<property name="PropName4" value="Hueseman Rd" />
<property name="PropName5" value="8540-8704" />
<property name="PropName6" value="47001" />
</bean>
For More Information
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-moxy-extension.html
http://blog.bdoughan.com/2011/03/map-to-element-based-on-attribute-value.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html

Related

Spring Ehcache 3.x configuration via XML

I'm trying to inject Ehcache (v3.9) javax.cache.CacheManager (JSR-107 JCache API) using Spring XML context.
Initial attempt:
<bean id="cacheManager" factory-bean="cachingManagerProvider" factory-method="getCacheManager">
<constructor-arg name="uri" type="java.net.URI" value="classpath:/WEB-INF/ehcache.xml" />
<constructor-arg name="classLoader" type="java.lang.ClassLoader" ><null /></constructor-arg>
</bean>
<bean id="cachingManagerProvider" class="org.ehcache.jsr107.EhcacheCachingProvider" />
fails with java.lang.IllegalArgumentException: Could not retrieve URI for class path resource [WEB-INF/ehcache.xml]: class path resource [WEB-INF/ehcache.xml] cannot be resolved to URL because it does not exist
I managed to do it only using adapter class:
import java.io.IOException;
import javax.cache.CacheManager;
import javax.cache.spi.CachingProvider;
import org.springframework.core.io.Resource;
public class CacheManagerFactory {
private final Resource configurationLocation;
private final CachingProvider cachingProvider;
public CacheManagerFactory(CachingProvider cachingProvider, Resource configurationLocation) {
this.configurationLocation = configurationLocation;
this.cachingProvider = cachingProvider;
}
public CacheManager getCacheManager() {
try {
return this.cachingProvider.getCacheManager(this.configurationLocation.getURI(), this.getClass().getClassLoader());
} catch (IOException ex) {
throw new RuntimeException("Missing configuration", ex);
}
}
}
and in Spring context:
<bean id="cacheManager" factory-bean="cacheManagerFactory" factory-method="getCacheManager" />
<bean id="cacheManagerFactory" class="com.test.CacheManagerFactory">
<constructor-arg name="cachingProvider" ref="cachingManagerProvider" />
<constructor-arg name="configurationLocation" value="/WEB-INF/ehcache.xml" />
</bean>
<bean id="cachingManagerProvider" class="org.ehcache.jsr107.EhcacheCachingProvider" />
The problem was specifying "/WEB-INF/ehcache.xml" as URI relative to webapp root as argument to org.ehcache.jsr107.EhcacheCachingProvider#getCacheManager() method.
Is it possible to inject without wrapper class?

How to add additional element to WADL?

I try to add an error object to response (with HTTP status 422). It works fine, but I want also to add the scheme of my error object to the automatically generated WADL.
Code:
JAX-B model classes:
#XmlRootElement(namespace = "http://www.test.com/test")
#XmlAccessorType(value = XmlAccessType.FIELD)
public class UnprocessableEntityError {
#XmlElement
private String key;
public String getKey() {
return key;
}
public void setKey(final String key) {
this.key = key;
}
}
#XmlRootElement(namespace = "http://www.test.com/")
public class TestModel {
}
JAX-RS resource class:
#Path("test")
public class TestResource {
#POST
public TestModel doSomething() {
throw new WebApplicationException("Error", Response.status(422).entity(new UnprocessableEntityError()).build());
}
}
CXF configuration:
<jaxrs:server address="/rest" id="test" staticSubresourceResolution="true">
<jaxrs:serviceBeans>
<ref bean="testResource" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="org.apache.cxf.jaxrs.provider.JAXBElementProvider" />
<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider" />
</jaxrs:providers>
</jaxrs:server>
WADL:
<?xml version="1.0"?>
<application xmlns:prefix1="http://www.test.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://wadl.dev.java.net/2009/02">
<grammars>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.test.com/" targetNamespace="http://www.test.com/" elementFormDefault="unqualified" attributeFormDefault="unqualified">
<xs:complexType name="testModel">
<xs:sequence/>
</xs:complexType>
</xs:schema>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.test.com/" targetNamespace="http://www.test.com/" elementFormDefault="unqualified" attributeFormDefault="unqualified">
<xs:import/>
<xs:element name="testModel" type="testModel"/>
</xs:schema>
</grammars>
<resources base="http://localhost:8080/test-app/services/rest/1">
<resource path="/test">
<method name="POST">
<response>
<representation mediaType="*/*" element="prefix1:testModel"/>
</response>
</method>
</resource>
</resources>
</application>
Is there any way, to add (just for documentation) an additional element to the grammar of an automatically generated WADL?
You can customize WADL automatic generation but is not very flexible. The general CXF documentation is here but is not helpful.
In the area of <grammars> you can include a custom XSD or link one
Define a WadlGenerator in CXF spring file, and include in the jaxrs provider. For example with books.xsd
<jaxrs:server address="/rest" id="test" >
<jaxrs:providers>
<ref bean="wadlGenerator" />
</jaxrs:providers>
</jaxrs:server>
<bean id="wadlGenerator" class="org.apache.cxf.jaxrs.model.wadl.WadlGenerator">
<property name="schemaLocations" value="classpath:/books.xsd"/>
</bean>
Programmatically is also possible
WadlGenerator wg = new WadlGenerator();
wg.setSchemaLocations(Collections.singletonList("classpath:/books.xsd"));
The generated WADL will be like this
<application xmlns="http://wadl.dev.java.net/2009/02" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<grammars>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://superbooks" attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://superbooks">
<xs:element name="thebook" type="tns:book"/>
<xs:complexType name="book">
<xs:sequence>
<xs:element minOccurs="0" name="chapter" type="xs:string"/>
<xs:element name="id" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</grammars>
Also, you can include a link
<bean id="wadlGenerator" class="org.apache.cxf.jaxrs.model.wadl.WadlGenerator">
<property name="externalLinks" value="http://books"/>
</bean>
WADL
<grammars>
<include href="http://books"/>
</grammars>
The full documentation of WadlGenerator. I have tested with CXF 2.7
EDITED
Custom WADL Generator
CXF WadlGenerator fills the 'grammars' section with XSD, external links or the autogenerated wadl, but do not allow to combine them. To add an external resource link to generated grammar is needed to create a custom WadlGenerator
<grammars>
<include href="http://books"/>
<!-- The autogenerated grammar-->
</grammars>
This is a fully functional class for CXF 2.7, but I hope it works at any later version because uses inheritance and rewrite only a minimal part of code. It concats externalLinks (if exists) with the autogenerated code or XSD
package com.wadl;
import java.net.URI;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBContext;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.jaxrs.model.ResourceTypes;
import org.apache.cxf.jaxrs.model.wadl.WadlGenerator;
public class CustomWadlGenerator extends WadlGenerator {
private List<URI> externalSchemaLinks;
private static final Logger LOG = LogUtils.getL7dLogger(CustomWadlGenerator.class);
//Overwrite setExternalLink so that it is not used in the superclass
#Override
public void setExternalLinks(List<String> externalLinks) {
externalSchemaLinks = new LinkedList<URI>();
for (String s : externalLinks) {
try {
String href = s;
if (href.startsWith("classpath:")) {
int index = href.lastIndexOf('/');
href = index == -1 ? href.substring(9) : href.substring(index + 1);
}
externalSchemaLinks.add(URI.create(href));
} catch (Exception ex) {
LOG.warning("Not a valid URI : " + s);
externalSchemaLinks = null;
break;
}
}
}
private class ExternalSchemaWriter implements WadlGenerator.SchemaWriter {
private List<URI>links;
private UriInfo uriInfo;
private SchemaWriter writer;
public ExternalSchemaWriter(List<URI>links, UriInfo ui, SchemaWriter writer){
this.links = links;
this.uriInfo = ui;
this.writer = writer;
}
public void write(StringBuilder sb) {
//write links
for (URI link : links) {
try {
URI value = link.isAbsolute() ? link : uriInfo.getBaseUriBuilder().path(link.toString()).build(new Object[0]);
sb.append("<include href=\"").append(value.toString()).append("\"/>");
} catch (Exception ex) {
CustomWadlGenerator.LOG.warning("WADL grammar section will be incomplete, this link is not a valid URI : " + link.toString());
}
}
//concat with default writer
writer.write(sb);
}
}
#Override
protected SchemaWriter createSchemaWriter(ResourceTypes resourceTypes, JAXBContext context, UriInfo ui) {
SchemaWriter schemaCollectionWriter = super.createSchemaWriter(resourceTypes, context, ui);
if (externalSchemaLinks == null){
//default behaviour
return schemaCollectionWriter;
} else {
//use custom writer
return new ExternalSchemaWriter(externalSchemaLinks,ui,schemaCollectionWriter);
}
}
}
Set WadlGenerator in spring config
<bean id="wadlGenerator" class="com.wadl.CustomWadlGenerator">
<property name="externalLinks" value="http://books"/>
</bean>

Rename element names for MOXy JSON marshalling

I use a common JAXB model for JAX-WS (Metro) and JAX-RS (Jersey). I have the following request snippet:
<xs:element name="CreatedProjFolders">
<xs:complexType>
<xs:sequence>
<xs:element name="CreatedProjFolder" type="tns:CreatedProjFolder" minOccurs="0"
maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="parentItemId" type="tns:itemId" use="required" />
</xs:complexType>
</xs:element>
<xs:complexType name="CreateProjFolder">
<xs:attribute name="itemId" type="tns:itemId" use="required" />
...
</xs:complexType>
XJC generated this class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"createProjFolders"
})
#XmlRootElement(name = "CreateProjFolders")
public class CreateProjFolders {
#XmlElement(name = "CreateProjFolder", required = true)
#NotNull
#Size(min = 1)
#Valid
// Name has been pluralized with JAXB bindings file
protected List<CreateProjFolder> createProjFolders;
#XmlAttribute(name = "parentItemId", required = true)
#NotNull
#Size(max = 128)
protected String parentItemId;
...
}
The appropriate JSON POST should look like:
{"parentItemId":"P5J00142301", "createProjFolders":[
{"itemId":"bogus"}
]}
but actually must look like:
{"parentItemId":"P5J00142301", "CreateProjFolder":[
{"itemId":"bogus"}
]}
How can rename the property name for JSON only resembling the one in Java (protected List<CreateProjFolder> createProjFolders)?
When MOXy is used as your JSON-binding provider the JSON keys will be the same as what is specfieid in the #XmlElement annotations. For example when you have:
#XmlElement(name = "CreateProjFolder", required = true)
protected List<CreateProjFolder> createProjFolders;
You will get:
{"parentItemId":"P5J00142301", "CreateProjFolder":[
{"itemId":"bogus"}
]}
If you want different names in JSON than in XML you can leverage MOXy's external mapping metadata to override what has been specified in the annotations:
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
After reading Blaise's post and the blog, it took me two days to come up with a working solution. First of all, the current status of MOXyJsonProviderand ConfigurableMoxyJsonProvider make it a pain-in-the-ass exprience to make it work and have never been designed for that.
My first test was to make a clean room implementation which is completely decoupled from Jersey and runs in a main method.
Here is the main method:
public static void main(String[] args) throws JAXBException {
Map<String, Object> props = new HashMap<>();
InputStream importMoxyBinding = MOXyTest.class
.getResourceAsStream("/json-binding.xml");
List<InputStream> moxyBindings = new ArrayList<>();
moxyBindings.add(importMoxyBinding);
props.put(JAXBContextProperties.OXM_METADATA_SOURCE, moxyBindings);
props.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
props.put(JAXBContextProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
JAXBContext jc = JAXBContext.newInstance("my.package",
CreateProjFolders.class.getClassLoader(), props);
Unmarshaller um = jc.createUnmarshaller();
InputStream json = MOXyTest.class
.getResourceAsStream("/CreateProjFolders.json");
Source source = new StreamSource(json);
JAXBElement<CreateProjFolders> create = um.unmarshal(source, CreateProjFolders.class);
CreateProjFolders folders = create.getValue();
System.out.printf("Used JAXBContext: %s%n", jc);
System.out.printf("Unmarshalled structure: %s%n", folders);
Marshaller m = jc.createMarshaller();
m.setProperty(MarshallerProperties.INDENT_STRING, " ");
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
System.out.print("Marshalled structure: ");
m.marshal(folders, System.out);
}
Here the json-binding.xml:
<?xml version="1.0" encoding="UTF-8"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="my.package"
xml-mapping-metadata-complete="false">
<xml-schema namespace="urn:namespace"
element-form-default="QUALIFIED" />
<java-types>
<java-type name="CreateProjFolders">
<xml-root-element />
<java-attributes>
<xml-element java-attribute="projFolders" name="createProjFolders" />
</java-attributes>
</java-type>
<java-type name="CreateProjFolder">
<java-attributes>
<xml-element java-attribute="access" name="access" />
</java-attributes>
</java-type>
<java-type name="Access">
<java-attributes>
<xml-element java-attribute="productionSites" name="productionSites" />
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
and a sample input file:
{"parentItemId":"some-id",
"createProjFolders":[
{"objectNameEn":"bogus", "externalProjectId":"123456",
"access":{"productionSites":["StackOverflow"], "user":"michael-o"}}
]
}
Unmarshalling and marshalling work flawlessly. Now, how to make it work with Jersey? You can't because you cannot pass JAXBContext properties.
You need to copy MOXy's MOXyJsonProvider and the entire source of Jersey Media MOXy except for XML stuff into a new Maven project because of the AutoDiscoverable feature. This package will replace the original dependency.
Apply the following patches. Patches aren't perfect and can be imporoved because some code is duplicate, thus redundant but that can be done in a ticket later.
Now confiure that in your Application.class:
InputStream importMoxyBinding = MyApplication.class
.getResourceAsStream("/json-binding.xml");
List<InputStream> moxyBindings = new ArrayList<>();
moxyBindings.add(importMoxyBinding);
final MoxyJsonConfig jsonConfig = new MoxyJsonConfig();
jsonConfig.setOxmMedatadataSource(moxyBindings);
ContextResolver<MoxyJsonConfig> jsonConfigResolver = jsonConfig.resolver();
register(jsonConfigResolver);
Now try it. After several calls on different models you will see JAXBExceptions with 'premature end of file'. You will ask you why?! The reason is that the MOXyJsonProvider creates and caches JAXBContexts per domain class and not per package which means that your input stream is read several times but has already been closed after the first read. You are lost. You need to reset the stream but cannot change the inner guts of MOXy. Here is a simple solution for that:
public class ResetOnCloseInputStream extends BufferedInputStream {
public ResetOnCloseInputStream(InputStream is) {
super(is);
super.mark(Integer.MAX_VALUE);
}
#Override
public void close() throws IOException {
super.reset();
}
}
and swap your Application.class for
moxyBindings.add(new ResetOnCloseInputStream(importMoxyBinding));
After you have felt the pain in the ass, enjoy the magic!
Final words:
I did not accept Blaise's answer (upvote given) because it was only a fraction of a solution but lead me into the right direction.
Both classes make it quite hard to solve a very simple problem, more suprisingly that I am appearantly the only one who wants to pass OXM_METADATA_SOURCE. Seriously?

Handle CData in Stax and jaxb

I'm trying to write a XML fragment containing a CData element in Spring StaxtItemWriter and Jaxb marshaller. But the output is either:
not wrap with <![CDATA[ ... ]]>
or escaped: < and > replaced by < and >
First, the item writer:
<bean id="userAuthorizationErrorItemWriter"
class="org.springframework.batch.item.xml.StaxEventItemWriter">
<property name="rootTagName" value="UserAuthorizationEvent" />
<property name="rootElementAttributes">
<map>
<entry key="xmlns:xsi" value="http://www.w3.org/2001/XMLSchema-instance" />
</map>
</property>
<property name="marshaller" ref="userAuthorizationTypeMarshaller" />
<property name="overwriteOutput" value="true" />
<property name="resource" value="file://C:/TEMP/fichierOUT.xml" />
<property name="transactional" value="false" />
</bean>
Then the JAXB marshaller
<bean id="userAuthorizationTypeMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="com.usecase1.xsd.model"/>
</bean>
The java class (simplified):
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "UserAuthorizationType", propOrder = {"action"})
#XmlRootElement(name = "UserAuthorization")
public class UserAuthorizationType {
#XmlElement(name = "Action", required = true)
protected String action;
public String getAction() {
return action;
}
public void setAction(String value) {
this.action = value;
}
}
When writing to file, I want that the Action value to be encapsulate into a <![CDATA[ ... ]]> block.
I tried using a #XmlJavaTypeAdapter(Adapter1.class) that return:
public String marshal(String value) {
return "<![CDATA[" + value + "]]>";
}
But the output is escaped:
<UserAuthorization><Action><![CDATA[first, second, third]]></Action></UserAuthorization>
Do you have any idea?
You will have to create a custom XML marshaller and register the elements that should be output as CDATA. See if this example helps https://jaxb.java.net/faq/JaxbCDATASample.java

Spring MVC generate quoted field name json output

I am using spring 3.0 in my webapplication. I've got recently a problem in my application. I am using <mvc:annotation-drive/> tag in my spring-servlet.xml file but due to a requirement I've to remove this and place XML configuration instead.
But now my problem is it generates json output with quoted field names like if I return Boolean.TRUE I got "true" in the output. I want just true without quotes.
here is my XML configuration
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
<bean id="pathMatcher" class="net.hrms.web.filters.CaseInsensitivePathMatcher" />
<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService"></property>
</bean>
</property>
<property name="messageConverters">
<list>
<ref bean="byteArrayConverter"/>
<ref bean="jaxbConverter"/>
<ref bean="jsonHttpMessageConverter"/>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
</list>
</property>
</bean>
<bean name="byteArrayConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
<bean name="jaxbConverter" class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
<bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="pathMatcher" ref="pathMatcher"></property>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsps/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="jsonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="prefixJson" value="false"/>
<property name="supportedMediaTypes" value="application/json"/>
</bean>
any help would be much appreciable.
If you are using the FlexJSON plugin, you can create custom output for JSON. I am sure you can do that in Jackson too, but I have never done it. There is loads of examples on the FlexJSON site.
What happens if you return just the primitive value of true (or Boolean.TRUE.booleanValue() rather than the wrapped object version of Boolean.TRUE?
I believe that true and false values are hardcoded in Jackson library (writeBoolean is called even for the boxed booleans):
private final static byte[] TRUE_BYTES = { 't', 'r', 'u', 'e' };
#Override
public void writeBoolean(boolean state)
throws IOException, JsonGenerationException
{
_verifyValueWrite("write boolean value");
if ((_outputTail + 5) >= _outputEnd) {
_flushBuffer();
}
byte[] keyword = state ? TRUE_BYTES : FALSE_BYTES;
int len = keyword.length;
System.arraycopy(keyword, 0, _outputBuffer, _outputTail, len);
_outputTail += len;
}
so Jackson will never return double-quoted "true" in field values(if its behavior is not heavily overriden, for example with codecs).
So please check that MappingJacksonHttpMessageConverter methods are being invoked, and conversion is not performed somewhere else.
That is the default behavior, if you want your boolean values unquoted use a JAXB ContextResolver
Something like this
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import com.sun.jersey.api.json.JSONJAXBContext;
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
private Class[] types = {SomeClass.class}; //Add the classes processed by JAXB and exposing boolean properties
public JAXBContextResolver() throws Exception {
Map props = new HashMap<String, Object>();
props.put(JSONJAXBContext.JSON_NOTATION, JSONJAXBContext.JSONNotation.MAPPED);
props.put(JSONJAXBContext.JSON_ROOT_UNWRAPPING, Boolean.TRUE);
java.util.HashSet<String> eprops = new HashSet<String>();
eprops.add("someBooleanProperty"); //add properties you want unquoted
props.put(JSONJAXBContext.JSON_NON_STRINGS, eprops);
this.context = new JSONJAXBContext(types, props);
}
public JAXBContext getContext(Class<?> objectType) {
return (types[0].equals(objectType)) ? context : null;
}
}

Categories