DATACOUCH-30 - ObjectMapper configuration must be supported

Allow for a custom ObjectMapper to be used.
This commit is contained in:
David Harrigan
2013-10-17 15:28:13 +01:00
committed by Michael Nitschinger
parent 51dea603de
commit 7d79d79e28
14 changed files with 252 additions and 69 deletions

View File

@@ -25,6 +25,8 @@ import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.util.ClassUtils;
@@ -56,7 +58,7 @@ public abstract class AbstractCouchbaseConfiguration {
*/
@Bean
public CouchbaseTemplate couchbaseTemplate() throws Exception {
return new CouchbaseTemplate(couchbaseClient(), mappingCouchbaseConverter());
return new CouchbaseTemplate(couchbaseClient(), mappingCouchbaseConverter(), translationService());
}
/**
@@ -69,6 +71,18 @@ public abstract class AbstractCouchbaseConfiguration {
return new MappingCouchbaseConverter(couchbaseMappingContext());
}
/**
* Creates a {@link TranslationService}.
*
* @return TranslationService, defaulting to JacksonTranslationService.
*/
@Bean
public TranslationService translationService() {
final JacksonTranslationService jacksonTranslationService = new JacksonTranslationService();
jacksonTranslationService.afterPropertiesSet();
return jacksonTranslationService;
}
/**
* Creates a {@link CouchbaseMappingContext} equipped with entity classes scanned from the mapping base package.
*
@@ -91,18 +105,12 @@ public abstract class AbstractCouchbaseConfiguration {
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
if (StringUtils.hasText(basePackage)) {
ClassPathScanningCandidateComponentProvider componentProvider =
new ClassPathScanningCandidateComponentProvider(false);
componentProvider.addIncludeFilter(
new AnnotationTypeFilter(Document.class));
componentProvider.addIncludeFilter(
new AnnotationTypeFilter(Persistent.class));
for (BeanDefinition candidate :
componentProvider.findCandidateComponents(basePackage)) {
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(),
AbstractCouchbaseConfiguration.class.getClassLoader()));
}
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false);
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(), AbstractCouchbaseConfiguration.class.getClassLoader()));
}
}
return initialEntitySet;
@@ -111,7 +119,7 @@ public abstract class AbstractCouchbaseConfiguration {
/**
* Return the base package to scan for mapped {@link Document}s. Will return the package name of the configuration
* class (the concrete class, not this one here) by default.
*
* <p/>
* <p>So if you have a {@code com.acme.AppConfig} extending {@link AbstractCouchbaseConfiguration} the base package
* will be considered {@code com.acme} unless the method is overridden to implement alternate behavior.</p>
*

View File

@@ -22,6 +22,7 @@ package org.springframework.data.couchbase.config;
* @author Michael Nitschinger
*/
public class BeanNames {
/**
* Refers to the "<couchbase:couchbase />" bean.
*/
@@ -31,4 +32,10 @@ public class BeanNames {
* Refers to the "<couchbase:template />" bean.
*/
static final String COUCHBASE_TEMPLATE = "couchbaseTemplate";
/**
* Refers to the "<couchbase:translation-service />" bean
*/
static final String TRANSLATION_SERVICE = "translationService";
}

View File

@@ -23,7 +23,7 @@ import org.springframework.data.repository.config.RepositoryConfigurationExtensi
/**
* {@link org.springframework.beans.factory.xml.NamespaceHandler} for Couchbase configuration.
*
* <p/>
* This handler acts as a container for one or more bean parsers and registers them. During parsing, the elements
* get analyzed and the appropriate registered parser is called.
*
@@ -36,11 +36,11 @@ public class CouchbaseNamespaceHandler extends NamespaceHandlerSupport {
*/
public final void init() {
RepositoryConfigurationExtension extension = new CouchbaseRepositoryConfigurationExtension();
registerBeanDefinitionParser("repositories", new RepositoryBeanDefinitionParser(extension));
registerBeanDefinitionParser("couchbase", new CouchbaseParser());
registerBeanDefinitionParser("jmx", new CouchbaseJmxParser());
registerBeanDefinitionParser("template", new CouchbaseTemplateParser());
registerBeanDefinitionParser("translation-service", new CouchbaseTranslationServiceParser());
}
}

View File

@@ -33,7 +33,7 @@ import java.util.List;
/**
* Parser for "<couchbase:couchbase />" bean definitions.
*
* <p/>
* The outcome of this bean definition parser will be a constructed {@link CouchbaseClient}.
*
* @author Michael Nitschinger
@@ -44,6 +44,7 @@ public class CouchbaseParser extends AbstractSingleBeanDefinitionParser {
* Defines the bean class that will be constructed.
*
* @param element the XML element which contains the attributes.
*
* @return the class type to instantiate.
*/
@Override
@@ -60,14 +61,11 @@ public class CouchbaseParser extends AbstractSingleBeanDefinitionParser {
@Override
protected void doParse(final Element element, final BeanDefinitionBuilder bean) {
String host = element.getAttribute("host");
bean.addConstructorArgValue(
convertHosts(StringUtils.hasText(host) ? host : CouchbaseFactoryBean.DEFAULT_NODE));
bean.addConstructorArgValue(convertHosts(StringUtils.hasText(host) ? host : CouchbaseFactoryBean.DEFAULT_NODE));
String bucket = element.getAttribute("bucket");
bean.addConstructorArgValue(
StringUtils.hasText(bucket) ? bucket : CouchbaseFactoryBean.DEFAULT_BUCKET);
bean.addConstructorArgValue(StringUtils.hasText(bucket) ? bucket : CouchbaseFactoryBean.DEFAULT_BUCKET);
String password = element.getAttribute("password");
bean.addConstructorArgValue(
StringUtils.hasText(password) ? password : CouchbaseFactoryBean.DEFAULT_PASSWORD);
bean.addConstructorArgValue(StringUtils.hasText(password) ? password : CouchbaseFactoryBean.DEFAULT_PASSWORD);
}
/**
@@ -76,36 +74,35 @@ public class CouchbaseParser extends AbstractSingleBeanDefinitionParser {
* @param element the XML element which contains the attributes.
* @param definition the bean definition to work with.
* @param parserContext encapsulates the parsing state and configuration.
*
* @return the ID to work with.
*/
@Override
protected String resolveId(final Element element, final AbstractBeanDefinition definition,
final ParserContext parserContext) {
protected String resolveId(final Element element, final AbstractBeanDefinition definition, final ParserContext parserContext) {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : BeanNames.COUCHBASE;
}
/**
* Convert a list of hosts into a URI format that can be used by the {@link CouchbaseClient}.
*
* <p/>
* To make it simple to use, the list of hosts can be passed in as a comma separated list. This list gets parsed
* and converted into a URI format that is suitable for the underlying {@link CouchbaseClient} object.
*
* @param hosts the host list to convert.
*
* @return the converted list with URIs.
*/
private List<URI> convertHosts(final String hosts) {
String[] split = hosts.split(",");
List<URI> nodes = new ArrayList<URI>();
final String[] split = hosts.split(",");
final List<URI> nodes = new ArrayList<URI>();
try {
for (int i = 0; i < split.length; i++) {
nodes.add(new URI("http://" + split[i] + ":8091/pools"));
for (final String aSplit : split) {
nodes.add(new URI("http://" + aSplit + ":8091/pools"));
}
} catch (URISyntaxException ex) {
throw new BeanCreationException("Could not convert host list." + ex);
}
return nodes;
}

View File

@@ -26,7 +26,7 @@ import org.w3c.dom.Element;
/**
* Parser for "<couchbase:template />" bean definitions.
*
* <p/>
* The outcome of this bean definition parser will be a constructed {@link CouchbaseTemplate}.
*
* @author Michael Nitschinger
@@ -39,11 +39,11 @@ public class CouchbaseTemplateParser extends AbstractSingleBeanDefinitionParser
* @param element the XML element which contains the attributes.
* @param definition the bean definition to work with.
* @param parserContext encapsulates the parsing state and configuration.
*
* @return the ID to work with.
*/
@Override
protected String resolveId(final Element element, final AbstractBeanDefinition definition,
final ParserContext parserContext) {
protected String resolveId(final Element element, final AbstractBeanDefinition definition, final ParserContext parserContext) {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : BeanNames.COUCHBASE_TEMPLATE;
}
@@ -52,6 +52,7 @@ public class CouchbaseTemplateParser extends AbstractSingleBeanDefinitionParser
* Defines the bean class that will be constructed.
*
* @param element the XML element which contains the attributes.
*
* @return the class type to instantiate.
*/
@Override
@@ -67,14 +68,19 @@ public class CouchbaseTemplateParser extends AbstractSingleBeanDefinitionParser
*/
@Override
protected void doParse(final Element element, final BeanDefinitionBuilder bean) {
String clientRef = element.getAttribute("client-ref");
String converterRef = element.getAttribute("converter-ref");
String dbRef = element.getAttribute("db-ref");
String translationServiceRef = element.getAttribute("translation-service-ref");
bean.addConstructorArgReference(StringUtils.hasText(dbRef) ? dbRef : BeanNames.COUCHBASE);
bean.addConstructorArgReference(StringUtils.hasText(clientRef) ? clientRef : BeanNames.COUCHBASE);
if (StringUtils.hasText(converterRef)) {
bean.addConstructorArgReference(converterRef);
}
if (StringUtils.hasText(translationServiceRef)) {
bean.addConstructorArgReference(translationServiceRef);
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Enables Parsing of the "<couchbase:translation-service />" configuration bean.
*
* @author David Harrigan
*/
public class CouchbaseTranslationServiceParser extends AbstractSingleBeanDefinitionParser {
/**
* Defines the bean class that will be constructed.
*
* @param element the XML element which contains the attributes.
*
* @return the class type to instantiate.
*/
@Override
protected Class getBeanClass(final Element element) {
return JacksonTranslationService.class;
}
/**
* Parse the bean definition and build up the bean.
*
* @param element the XML element which contains the attributes.
* @param bean the builder which builds the bean.
*/
@Override
protected void doParse(final Element element, final BeanDefinitionBuilder bean) {
final String objectMapper = element.getAttribute("objectMapper");
bean.addPropertyValue("objectMapper", StringUtils.hasText(objectMapper) ? objectMapper : new ObjectMapper());
}
/**
* Resolve the bean ID and assign a default if not set.
*
* @param element the XML element which contains the attributes.
* @param definition the bean definition to work with.
* @param parserContext encapsulates the parsing state and configuration.
*
* @return the ID to work with (e.g., "translationService")
*/
@Override
protected String resolveId(final Element element, final AbstractBeanDefinition definition, final ParserContext parserContext) {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : BeanNames.TRANSLATION_SERVICE;
}
}

View File

@@ -26,6 +26,7 @@ import org.springframework.dao.QueryTimeoutException;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
@@ -53,7 +54,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
protected final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
private static final Collection<String> ITERABLE_CLASSES;
private final CouchbaseExceptionTranslator exceptionTranslator = new CouchbaseExceptionTranslator();
private final JacksonTranslationService translationService;
private final TranslationService<String> translationService;
static {
final Set<String> iterableClasses = new HashSet<String>();
@@ -64,14 +65,21 @@ public class CouchbaseTemplate implements CouchbaseOperations {
}
public CouchbaseTemplate(final CouchbaseClient client) {
this(client, null);
this(client, null, null);
}
public CouchbaseTemplate(final CouchbaseClient client, final CouchbaseConverter converter) {
public CouchbaseTemplate(final CouchbaseClient client, final CouchbaseConverter couchbaseConverter, final TranslationService translationService) {
this.client = client;
couchbaseConverter = converter == null ? getDefaultConverter() : converter;
mappingContext = couchbaseConverter.getMappingContext();
translationService = new JacksonTranslationService();
this.couchbaseConverter = couchbaseConverter == null ? getDefaultConverter() : couchbaseConverter;
this.translationService = translationService == null ? getDefaultTranslationService() : translationService;
mappingContext = this.couchbaseConverter.getMappingContext();
}
public CouchbaseTemplate(final CouchbaseClient client, final TranslationService translationService) {
this.client = client;
this.couchbaseConverter = couchbaseConverter == null ? getDefaultConverter() : couchbaseConverter;
this.translationService = translationService == null ? getDefaultTranslationService() : translationService;
mappingContext = this.couchbaseConverter.getMappingContext();
}
private CouchbaseConverter getDefaultConverter() {
@@ -80,6 +88,12 @@ public class CouchbaseTemplate implements CouchbaseOperations {
return converter;
}
private TranslationService getDefaultTranslationService() {
final JacksonTranslationService jacksonTranslationService = new JacksonTranslationService();
jacksonTranslationService.afterPropertiesSet();
return jacksonTranslationService;
}
private Object translateEncode(final CouchbaseStorable source) {
return translationService.encode(source);
}

View File

@@ -16,8 +16,14 @@
package org.springframework.data.couchbase.core.convert.translation;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbaseList;
import org.springframework.data.couchbase.core.mapping.CouchbaseStorable;
@@ -32,7 +38,12 @@ import java.util.Map;
*
* @author Michael Nitschinger
*/
public class JacksonTranslationService implements TranslationService {
public class JacksonTranslationService implements TranslationService, InitializingBean {
/**
* Jackson Object Mapper;
*/
private ObjectMapper objectMapper;
/**
* Type holder to help easily identify simple types.
@@ -92,7 +103,7 @@ public class JacksonTranslationService implements TranslationService {
if (simpleTypeHolder.isSimpleType(clazz) && !isEnumOrClass(clazz)) {
generator.writeObject(value);
} else {
new ObjectMapper().writeValue(generator, value);
objectMapper.writeValue(generator, value);
}
}
@@ -221,5 +232,15 @@ public class JacksonTranslationService implements TranslationService {
}
}
public void setObjectMapper(final ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public void afterPropertiesSet() {
if (objectMapper == null) {
objectMapper = new ObjectMapper();
}
}
}

View File

@@ -1,19 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://www.springframework.org/schema/data/couchbase"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:repository="http://www.springframework.org/schema/data/repository"
targetNamespace="http://www.springframework.org/schema/data/couchbase"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:import namespace="http://www.springframework.org/schema/tool" />
<xsd:import namespace="http://www.springframework.org/schema/context" />
<xsd:import namespace="http://www.springframework.org/schema/data/repository"
schemaLocation="http://www.springframework.org/schema/data/repository/spring-repository-1.0.xsd" />
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
<xsd:import namespace="http://www.springframework.org/schema/context"/>
<xsd:import namespace="http://www.springframework.org/schema/data/repository" schemaLocation="http://www.springframework.org/schema/data/repository/spring-repository-1.0.xsd"/>
<xsd:element name="couchbase" type="couchbaseType">
<xsd:annotation>
@@ -44,7 +40,19 @@ Defines a CouchbaseClient instance used for accessing a Couchbase Cluster.
<xsd:attribute name="id" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the couchbase definition (by default "couchbaseFactory").]]></xsd:documentation>
The id of the couchbase definition (by default "couchbaseFactory").]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="client-ref" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
The reference to a CouchbaseClient object.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="com.couchbase.client.CouchbaseClient"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="converter-ref" type="converterRef" use="optional">
@@ -60,16 +68,37 @@ The reference to a CouchbaseConverter instance.
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="db-ref" type="xsd:string"
use="optional">
<xsd:attribute name="translation-service-ref" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
The reference to a CouchbaseClient object.
<xsd:documentation><![CDATA[
The reference to the Translation Service (by default "translationService").]]>
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to
type="com.couchbase.client.CouchbaseClient" />
<tool:assignable-to type="org.springframework.data.couchbase.core.convert.translation.TranslationService"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="translation-service">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The id of the translation-service definition (by default "translationService").]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="objectMapper" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a configured Jackson ObjectMapper.]]>
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="com.fasterxml.jackson.databind.ObjectMapper"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>

View File

@@ -66,14 +66,12 @@ public class TestApplicationConfig extends AbstractCouchbaseConfiguration {
return new BucketCreator(couchbaseHost(), couchbaseAdminUser(), couchbaseAdminPassword());
}
@Override
@Bean
@Override
@DependsOn("bucketCreator")
public CouchbaseClient couchbaseClient() throws Exception {
return new CouchbaseClient(
Arrays.asList(new URI(couchbaseHost())), couchbaseBucket(), couchbasePassword());
return new CouchbaseClient(Arrays.asList(new URI(couchbaseHost())), couchbaseBucket(), couchbasePassword());
}
}

View File

@@ -20,6 +20,7 @@ import com.couchbase.client.CouchbaseClient;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.data.couchbase.TestApplicationConfig;
import org.springframework.data.couchbase.core.mapping.Document;
@@ -47,14 +48,13 @@ public class AbstractCouchbaseConfigurationTests {
@Test
public void usesConfigClassPackageAsBaseMappingPackage() throws Exception {
AbstractCouchbaseConfiguration config = new SampleCouchbaseConfiguration();
assertEquals(config.getMappingBasePackage(),
SampleCouchbaseConfiguration.class.getPackage().getName());
assertEquals(config.getMappingBasePackage(), SampleCouchbaseConfiguration.class.getPackage().getName());
assertEquals(config.getInitialEntitySet().size(), 1);
assertTrue(config.getInitialEntitySet().contains(Entity.class));
}
class SampleCouchbaseConfiguration extends AbstractCouchbaseConfiguration {
@Bean
@Override
public CouchbaseClient couchbaseClient() throws Exception {
@@ -64,5 +64,6 @@ public class AbstractCouchbaseConfigurationTests {
@Document
static class Entity {
}
}

View File

@@ -50,4 +50,14 @@ public class CouchbaseTemplateParserIntegrationTests {
factory.getBean("couchbaseTemplate");
}
@Test
public void readsCouchbaseTemplateWithTranslationServiceAttributesCorrectly() {
reader.loadBeanDefinitions(new ClassPathResource("namespace/couchbase-template-with-translation-service-bean.xml"));
BeanDefinition definition = factory.getBeanDefinition("couchbaseTemplate");
assertEquals(2, definition.getConstructorArgumentValues().getArgumentCount());
factory.getBean("couchbaseTemplate");
}
}

View File

@@ -7,6 +7,10 @@
<couchbase:couchbase/>
<couchbase:couchbase id="couchbase2" host="localhost" bucket="default" password="" />
<couchbase:couchbase id="couchbase2" host="localhost" bucket="default" password=""/>
<couchbase:translation-service objectMapper="myCustomObjectMapper"/>
<bean id="myCustomObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper"/>
</beans>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:couchbase="http://www.springframework.org/schema/data/couchbase"
xsi:schemaLocation="http://www.springframework.org/schema/data/couchbase http://www.springframework.org/schema/data/couchbase/spring-couchbase.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<couchbase:couchbase/>
<couchbase:template translation-service-ref="myCustomTranslationService"/>
<bean id="myCustomTranslationService" class="org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService"/>
</beans>