2012-06-03 2 views
3

Ниже приведен фрагмент из проекта на основе Spring Web MVC 3.1.1. Сериализация Json производится через Jackson.Spring 3.1.1 web MVC - ограничение ответа на интерфейсные модули

У меня есть контроллер, который сопоставляется с URL-адресом, и все работает нормально.

@Controller 
@RequestMapping("/vod") 
public class VODController { 
private Configuration configuration; 
private SearchAPI  searchAPI; 

@RequestMapping(method = RequestMethod.GET, params = "cmd=list") 
public @ResponseBody GetAssetsReply listVODAssets(long offset, int limit) { 
    SearchVODAssetRequest searchVODAssetRequest = new SearchVODAssetRequest(); 
    //.... some irrelevant code 
    return searchAPI.searchVODAssets(searchVODAssetRequest); 
} 
} 

И это GetAssetsReply:

public class GetAssetsReply { 
    private long totalAssets; 
    private List<VODAsset> assets = new LinkedList<VODAsset>(); 

    // Getters and setters removed for simplicity 
} 

VODAsset является интерфейсом:

public interface VODAsset { 
    public String getName(); 
} 

И это его реализация:

public class AssetElement implements VODAsset { 
    private String  id; 
    private String  name; 
    private double  duration; 

    // Getters and setters removed for simplicity 
} 

Наконец к вопрос: Контроллер возвращает мне ожидаемый результат с одной нижней стороной - он возвращает активы VOD с его идентификатором и длительностью в дополнение к его имени. Я бы ожидал получить только имя из-за того, что объект указан указанным выше интерфейсом VODAsset. Как я могу получить это поведение? Любая помощь была бы очень благодарна

ответ

4

Если я правильно понял ваш вопрос, и вы используете Jackson для преобразования результата в JSON, то вы можете использовать org.codehaus.jackson.annotate.JsonIgnore, чтобы избежать поля, которое будет перенаправлено в результат JSON. (Генри). Кроме того, можно добавить к интерфейсу @JsonAutoDetect(JsonMethod.NONE), из-за чего Джексон не будет автоматически искать поля для сериализации, а затем добавить @JsonProperty в поля, которые действительно необходимы для сериализации (практически реализуя схему белого списка для поля Джексона стратегия сериализации).

Вот пример кода, который решает указанную выше проблему:

@JsonAutoDetect(JsonMethod.NONE) //This tells the json serializer not to search for properties to serialize 
public interface VODAsset { 
    @JsonProperty //This tells the json serializer that this is a property that it should serialize 
    public String getName(); 
} 

С другой стороны каждого поля игнорировать схема может быть реализована следующим образом:

Маркер аннотацию, который указывает, что аннотированный метод или поле: , которое должно быть проигнорировано сериализацией и десериализацией на основе интроспекции . То есть, это не следует рассматривать как «геттер», «сеттер» или «создатель».

@JsonIgnore 
public String getId() { 
    return id; 
} 
+0

Благодарим за быстрый ответ. Я использовал 2 вещи: 1. Я добавил на интерфейс '@JsonAutoDetect (JsonMethod.NONE)' 2. Я добавил в метод 'getName' интерфейса' @ JsonProperty' интерфейса, который эффективно удалил все остальные поля из стратегии сериализации json , Должен ли я отредактировать ваш ответ и принять его? не знаю, как это работает ..... – henryabra

+0

и да, я не упоминал об этом на вопрос о Джексоне. Nice catch :) – henryabra

+0

@henryabra Вы можете обновить ответ. :) –

0

Реализации, I.e., классы возвращаются на основе конфигурации Spring bean. Если вы хотите получить другой результат, верните другую реализацию. Поэтому, если у AssetElement есть реквизиты, которые вы не хотите раскрывать, создайте класс aanother, который будет использовать ваш интерфейс и только вернет эту опцию. Используйте это вместо своего GetAssetsReply.

+0

Спасибо за ваш быстрый ответ. – henryabra

2

выше ответ (используя @JsonProperty и @JsonIgnore) пары вашей модели данных для конкретного случая использования, и если вы когда-нибудь понадобится, чтобы вернуть другую проекцию этого объекта (например, свойство ID) его означало бы создание другого класса или изменение аннотаций и перекомпиляцию этого класса.

Обычно я предпочитаю возвращать карту, которую я заполняю явно для каждого варианта использования. например

Map<String, Object> responseBody = new HashMap<String, Object>(); 
/* 
JSON format: 
{ 
    "prop1":"<value1>", 
    "edition":"<value2>", 
    "dateProp":"<formatted date>" 
} 
*/ 
responseBody.put("prop1", value1); 
responseBody.put("prop2", value2); 
responseBody.put("dateProp", dateFormat.format(expirationDate)); 
return new ResponseEntity<Map<String, Object>>(responseBody, HttpStatus.OK); 

Natrually вы также можете использовать @ResponseBody вместо явного создания ResposeEntity.

+0

, но опять же, мне не нужно было использовать эту очень полезную функцию для возврата любого объекта, который мне нравится (с @ResponseBody) – henryabra

+0

Я закончил создание карт, чтобы возвращать подмножества или специализации существующих объектов. – Jason

1

Я создал общее решение с использованием делегирования прокси. Это решило несколько проблем:

  • вы можете иметь различные интерфейсы на одной и той же реализации, которые вскрывают различные детали одного и того же объекта (например, IPersonList для коллекций, IPersonDetail, которая простирается IPersonList для деталей
  • вам не нужно. использовать @Json ... аннотаций в классах сущностей

Пример кода (с использованием Викисклада прокси):

/** 
* Wraps an object as delegating proxy, exposing only the methods of the interface 
**/ 
@SuppressWarnings("unchecked") 
protected <T> T wrap(Object sourceObject, Class<T> targetInterface) 
{ 
    return (T) new ProxyFactory().createDelegatorProxy(
      new ConstantProvider(sourceObject), 
      new Class[] {targetInterface}); 
} 

сейчас вы можете использовать

return wrap(assetElement, VODAsset.class) 

, чтобы получить «вид» типа VODAsset вашего элемента.

Следующим шагом будет создание прокси-сервера, который проверяет возвращаемые значения. Один из них - это интерфейс, он также должен создать прокси для возвращаемого значения. Также требуется специальная обработка списков (например, для вашего примера списка VODAsset). Таким образом, вы должны создать прокси рекурсивно.

Я сделал несколько тестов производительности путем создания 1.000.000 прокси в цикле после создания 100 прокси как Jvm "разминкой":

  • Reflection Proxies Java (Использование ProxyFactory): ~ 1200ms
  • CGLIB Proxies (Использование CglibProxyFactory): ~ 3900ms
  • Javassist Доверенное (Использование JavassistProxyFactory): ~ 470ms

Таким образом, используя в качестве прокси-Javaasist завод, накладных расходов на создание таких прокси является умеренный.

+0

Это выглядит очень элегантно. В следующий раз, когда мне нужно проецировать данные из объекта, я буду пытаться это сделать. – henryabra

Смежные вопросы