2016-06-15 34 views
10

Bahar 4.1.x MVC uygulamasına java.lang.String için özel bir Jackson deserializer eklemem gerekiyor. Ancak, tüm yanıtlar (this gibi), tüm web uygulaması için ObjectMapper'ın yapılandırılmasını ifade eder ve tüm denetleyicilerdeki tüm @RequestBody'de değişiklikler tüm Dizeler için geçerli olacaktır.Yay denetleyicisine özel Jackson serileştirmesini yapılandırabilir misiniz?

Yalnızca özel serileştirmeyi belirli denetleyicilerde kullanılan @RequestBody argümanlarına uygulamak istiyorum. Belirli String alanları için @JsonDeserialize ek açıklamalarını kullanma seçeneğim olmadığını unutmayın.

Yalnızca belirli denetleyiciler için özel seri hale getirme yapılandırabilirsiniz mı?

+0

Nesne eşleyicisi yazma hakkında ne düşünüyorsunuz? İçinde ihtiyacınız olan serileştirme mantığını ekleyebilirsiniz. – reos

+0

Sorun, bir nesne eşleyicisi oluşturmuyor. Sorum şu: web uygulaması içinde global olarak değil, bir nesne eşleyicisini denetleyici bazında nasıl yapılandırabilirim. – Mark

+0

Sorunuzu anlıyorum, tüm denetleyicilerinde kullanılacak bir nesne eşleştiricisi yazmayı öneririm, ancak isteğine bağlı olarak nesneyi seri hale getirebilir. – reos

cevap

-2

Serpiştirmek istediğiniz her farklı istek parametresi türü için bir POJO tanımlayabilirsiniz. Ardından, aşağıdaki kod, JSON'daki değerleri POJO'nuzdaki alanların adlarının JSON isteğindeki alanın adlarıyla eşleştiğini varsayarak, tanımladığınız nesneye çeker.

ObjectMapper mapper = new ObjectMapper(); 
YourPojo requestParams = null; 

try { 
    requestParams = mapper.readValue(JsonBody, YourPOJO.class); 

} catch (IOException e) { 
    throw new IOException(e); 
} 
+1

POJO'lar zaten var. İçindeki belirli alanların dersiçi hale getirilmesini, denetleyiciye özel bir temelde ve POJO'lara dokunmadan özelleştirmeyi düşünüyorum. – Mark

0

Muhtemelen bu yardımcı olabilir, ama hoş değil. AOP gerektirir. Ayrıca ben doğrulamadım. @CustomAnnotation oluşturun.

Güncelleme Denetleyiciniz:

void someEndpoint(@RequestBody @CustomAnnotation SomeEntity someEntity); 

Sonra AOP kısmını implemment:

@Around("execution(* *(@CustomAnnotation (*)))") 
public void advice(ProceedingJoinPoint proceedingJoinPoint) { 
    // Here you would add custom ObjectMapper, I don't know another way around it 
    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); 
    String body = request .getReader().lines().collect(Collectors.joining(System.lineSeparator())); 

    SomeEntity someEntity = /* deserialize */; 
    // This could be cleaner, cause the method can accept multiple parameters 
    proceedingJoinPoint.proceed(new Object[] {someEntity}); 
} 
+0

Yanılmıyorsam, bu yaklaşım aşağıdaki gibi davranacaktır: Spring, mevcut dönüştürücüyü istek gövdesini okumak için kullanacak ve ancak bu tavsiyenin sonucu bırakarak ve isteği tekrar okumaya başlayacaktır. İsteğin iki kez dönüştürülmesinin yanısıra, bunun iki yan etkisi vardır: Muhtemelen 'getRoader'de' getInputStream''de (sabitlenebilir) 'IllegalStateException' olacaktır ve mevcut dönüştürücü mümkün değilse' HttpMessageNotReadableException 'olacaktır isteği okumak için – chimmi

0

Sen Message Converters deneyebilirsiniz. http giriş isteği hakkında bir bağlam var (örneğin, belgeler bkz. here, JSON). Nasıl özelleştirilir here görebiliyordu. HttpInputMessage ürününü denetleyicilerinizde kullanabileceğiniz ve dizeyi istediğiniz gibi dönüştüren özel URI'lerle kontrol edebildiğinizi düşünün. Bunun için özel bir açıklama oluşturabilir, paketleri tarayabilir ve otomatik olarak yapabilirsiniz.

Not

Olasılıkla, sen ObjectMappers uygulanmasını gerekmez. String'i ayrıştırmak ve dizeyi istediğiniz gibi dönüştürmek için basit varsayılan ObjectMapper'ı kullanabilirsiniz. Bu durumda RequestBody'yi bir kez oluşturabilirsiniz.

+0

What 'bir MessageController'? – chimmi

+0

Teşekkür ederim, yazım hatası, düzeltildi. – egorlitvinenko

4

Farklı serileştirme yapılandırmalarına sahip olmanız için farklı ObjectMapper örneklerine sahip olmanız gerekir, ancak yay kutusunun dışında, yalnızca tek bir örneği kullanmak üzere tasarlanmış MappingJackson2HttpMessageConverter kullanır. Bir ArgumentResolver

uzakta MessageConverter dan

Taşı bir @CustomRequestBody ek açıklama oluşturun ve bir argüman çözümleyici:

Burada en az iki seçenek görmek

public class CustomRequestBodyArgumentResolver implements HandlerMethodArgumentResolver { 

    private final ObjectMapperResolver objectMapperResolver; 

    public CustomRequestBodyArgumentResolver(ObjectMapperResolver objectMapperResolver) { 
    this.objectMapperResolver = objectMapperResolver; 
    } 

    @Override 
    public boolean supportsParameter(MethodParameter methodParameter) { 
    return methodParameter.getParameterAnnotation(CustomRequestBody.class) != null; 
    } 

    @Override 
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { 
    if (this.supportsParameter(methodParameter)) { 
     ObjectMapper objectMapper = objectMapperResolver.getObjectMapper(); 
     HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); 
     return objectMapper.readValue(request.getInputStream(), methodParameter.getParameterType()); 
    } else { 
     return WebArgumentResolver.UNRESOLVED; 
    } 
    } 
} 

ObjectMapperResolver bir arayüz Kullanmak için gerçek ObjectMapper örneğini çözmek için kullanacağız, aşağıda tartışacağız. Elbette, özel eşlemeye ihtiyacınız olan tek bir kullanım durumunuz varsa, eşleştirmeyi buraya kolayca başlatabilirsiniz.

Bu yapılandırmayla özel argüman çözümleyicinizi ekleyebilirsiniz:

@Configuration 
public class WebConfiguration extends WebMvcConfigurerAdapter { 

    @Bean 
    public CustomRequestBodyArgumentResolver customBodyArgumentResolver(ObjectMapperResolver objectMapperResolver) { 
    return new CustomRequestBodyArgumentResolver(objectMapperResolver) 
    } 

    @Override 
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {  
    argumentResolvers.add(customBodyArgumentResolver(objectMapperResolver())); 
    } 
} 

Not:@RequestBody ile @CustomRequestBody birleştirmek etmeyin, dikkate alınmayacaktır. birden çok örneği

MappingJackson2HttpMessageConverter gizleyen bir vekil içinde

Wrap ObjectMapperObjectMapper sadece bir örneği ile çalışmak üzere tasarlanmıştır. Bu örneği bir vekil delege yapabiliriz. Bu, çoklu haritalayıcılarla çalışmayı şeffaf hale getirecektir.

Her şeyden önce, tüm yöntem çağrılarını altta yatan bir nesneye çevirecek bir ara kesiciye ihtiyacımız var.

public abstract class ObjectMapperInterceptor implements MethodInterceptor { 

    @Override 
    public Object invoke(MethodInvocation invocation) throws Throwable { 
    return ReflectionUtils.invokeMethod(invocation.getMethod(), getObject(), invocation.getArguments()); 
    } 

    protected abstract ObjectMapper getObject(); 

} 

Şimdi bizim ObjectMapper vekil fasulye gibi görünecektir:

@Bean 
public ObjectMapper objectMapper(ObjectMapperResolver objectMapperResolver) { 
    ProxyFactory factory = new ProxyFactory(); 
    factory.setTargetClass(ObjectMapper.class); 
    factory.addAdvice(new ObjectMapperInterceptor() { 

     @Override 
     protected ObjectMapper getObject() { 
     return objectMapperResolver.getObjectMapper(); 
     } 

    }); 

    return (ObjectMapper) factory.getProxy(); 
} 

Not: Ben, bu yüzden dolayı onun modüler sınıf yükleme Wildfly, bu proxy ile sınıf yükleme sorunları vardıObjectMapper'u (herhangi bir şeyi değiştirmeden) genişleterek sınıfımı modülümden kullanabilirim.

bir arada, bu yapılandırma ile bağlanmış tüm:

@Configuration 
public class WebConfiguration extends WebMvcConfigurerAdapter { 

    @Bean 
    public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() { 
    return new MappingJackson2HttpMessageConverter(objectMapper(objectMapperResolver())); 
    } 

    @Override 
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { 
    converters.add(jackson2HttpMessageConverter()); 
    } 
} 

ObjectMapperResolver uygulamaları

Nihai parça kullanılması gerektiğini eşleyici tespit mantığı, bir ObjectMapperResolver arayüz içinde ihtiva edilecektir. özel haritacılarla kullanım durumları çok yoksa

public interface ObjectMapperResolver { 

    ObjectMapper getObjectMapper(); 

} 

basitçe tuşları olarak ReqeustMatcher s ile önceden yapılandırılmış durumlarda bir haritasını yapabiliriz: Sadece bir bakış yukarı yöntem içerir. Böyle bir şey:

public class RequestMatcherObjectMapperResolver implements ObjectMapperResolver { 

    private final ObjectMapper defaultMapper; 
    private final Map<RequestMatcher, ObjectMapper> mapping = new HashMap<>(); 

    public RequestMatcherObjectMapperResolver(ObjectMapper defaultMapper, Map<RequestMatcher, ObjectMapper> mapping) { 
    this.defaultMapper = defaultMapper; 
    this.mapping.putAll(mapping); 
    } 

    public RequestMatcherObjectMapperResolver(ObjectMapper defaultMapper) { 
    this.defaultMapper = defaultMapper; 
    } 

    @Override 
    public ObjectMapper getObjectMapper() { 
    ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 
    HttpServletRequest request = sra.getRequest(); 
    for (Map.Entry<RequestMatcher, ObjectMapper> entry : mapping.entrySet()) { 
     if (entry.getKey().matches(request)) { 
     return entry.getValue(); 
     } 
    } 
    return defaultMapper; 
    } 

} 

Ayrıca isteği kullanabilirsiniz ObjectMapper kapsamlı ve daha sonra her istek temelinde yapılandırın. Bu yapılandırmayı kullanın:

@Bean 
public ObjectMapperResolver objectMapperResolver() { 
    return new ObjectMapperResolver() { 
    @Override 
    public ObjectMapper getObjectMapper() { 
     return requestScopedObjectMapper(); 
    } 
    }; 
} 


@Bean 
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) 
public ObjectMapper requestScopedObjectMapper() { 
    return new ObjectMapper(); 
} 

sağ denetleyici yönteminde yapılandırabilirsiniz, bu durum özel bir yanıt serileştirme için uygundur. Özel serileştirme için, denetleyici yöntemi tetiklenmeden önce geçerli eşleştirmeyi etkin eşleştiriciyi yapılandırmak için Filter/HandlerInterceptor/HandlerInterceptor/ControllerAdvice'u da kullanmanız gerekir.

public interface ObjectMapperConfigurer { 

    void configureObjectMapper(ObjectMapper objectMapper); 

} 

Sonra tuşları olarak RequstMatcher s ile bu durumlarda bir haritasını yapmak ve onu RequestMatcherObjectMapperResolver bir Filter/HandlerInterceptor/ControllerAdvice benzer koymak:

Sen ObjectMapperResolver benzer bir arayüz oluşturabilir.

P.S.Dinamik ObjectMapper yapılandırmasını biraz daha keşfetmek isterseniz, eski cevabımı here önerebilirim. Çalışma zamanında dinamik @JsonFilter s nasıl yapabileceğinizi açıklar. Ayrıca, yorumlarda önerdiğim genişletilmiş MappingJackson2HttpMessageConverter ile daha eski yaklaşımımı içerir.

0

Dize verileriniz için özel deserializer oluşturabilirsiniz.

Özel Deserializer

public class CustomStringDeserializer extends JsonDeserializer<String> { 

    @Override 
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { 

    String str = p.getText(); 

    //return processed String 
    } 

}

Şimdi dize değişkeni üzerinde bir POJO kullanım @JsonDeserialize açıklama içeride mevcut olduğunu varsayalım: Bir yanıt olarak döndürülebilir Şimdi

public class SamplePOJO{ 
    @JsonDeserialize(using=CustomStringDeserializer.class) 
    private String str; 
    //getter and setter 
} 

CustomDeserializer'da yaptığınız şekilde Deserialized olacak.

Yardım edin.

+0

Evet ama bir gereklilik POJO'ları değiştirmemdi. – Mark

İlgili konular