2013-01-22 4 views
0

Как я могу сказать, что Jackson использует другой сериализатор для каждого типа коллекции Generic. У меня есть CustomProductsSerializer и CustomClientsSerializerПолиморфная сериализация коллекций с пользовательским сериализатором в jackson

public class ProductsListSerializer extends JsonSerializer<List<Products>>{ 

    // logic to insert __metadata , __count into json 
    public void serialize(List<Products> value, JsonGenerator jgen, SerializerProvider provider) { 
    .... .... .... 
    } 
    // always returns ArrayList.class 
    public Class<List<Instrument>> handledType() { 
    Class c = var.getClass(); 
    return c; 
    } 
    } 


    public class CustomClientsSerializer extends JsonSerializer<List<Clients>>{ 

    // logic to insert __metadata , __count into json 
    public void serialize(List<Clients> value, JsonGenerator jgen, SerializerProvider provider){ 
    .... .... .... 
    } 
    .... .... .... 
    // always returns ArrayList.class 
    public Class<List<Clients>> handledType() { 
    Class c = var.getClass(); 
    return c; 
    } 
    } 

Я зарегистрировал оба сериализаторы с помощью SimpleModule. Проблема заключается в том, что поскольку handledType в обоих случаях возвращает ArrayList.class, сериализация нижеприведенных клиентов с классом исключений исключений.

List<Clients> clients; 
    // code to get clients from database. 
    String jsonString = mapper.writeValueAsString(clients); 

Вопрос в том, как рассказать о джексоне, о котором использовать сериализатор?

Я использую jackson как сериализатор json с resteasy.

- Удалить в ответ на @ HiJon89 У меня есть бизнес-объект, который возвращает «Список» и другой, который возвращает List. То, как мне нужно, - это когда объект Busines возвращает «Список», а затем «ProductListSerializer» должен использоваться для всего списка. и когда бизнес-объект возвращает «Список CustomClientsSerializer должен использоваться для всего списка. В моем случае использования я добавляю дополнительные элементы в сериализованный json 'eg: __ count, __metadata, __references'. В коллекции есть только один __count. Contentusing Может использоваться только для свойств. Какие-либо предложения ?

+0

Кажется, проблема может быть решена путем регистрации сериализатора для данного JavaType (com.fasterxml.jackson.databind). Но simpleModule.addSerializer() принимает два параметра Class и Serilaizer. Я не могу найти метод, который может принимать JavaType. –

ответ

0

Это возможно, расширив простой модем или расширив com.fasterxml.jackson.databind.Module. Я взял второй подход. Ниже приведены сводные данные об изменениях

Добавлен метод принятия JavaType в качестве параметра.

public <T> TypedModule addSerializer(JavaType type, JsonSerializer<T> ser) 
{ 
    if (_serializers == null) { 
     _serializers = new TypedSerializers(); 
    } 
    _serializers.addSerializer(type, ser); 
    return this; 
} 

выше метод добавляет новый serilaizer к

protected TypedSerializers _serializers = null; 

The «TypedSerializers класса расширяет сериализаторы.Base "хранит все типизированные сериализаторы в HashMap.

Изменена логика метода findSerializer в TypedSerializers

public JsonSerializer<?> findSerializer(SerializationConfig config, 
     JavaType type, BeanDescription beanDesc) { 

    Class<?> cls = type.getRawClass(); 
    ClassKey key = new ClassKey(cls); 
    JsonSerializer<?> ser = null; 
    if (_javaTypeMappings != null) { 
     ser = _javaTypeMappings.get(type.toString()); 
     if (ser != null) { 
      return ser; 
     } 
    } 
..... process normal serilaizers ... 

Ниже изменения необходимы при регистрации customserializer

TypedModule simpleModule = new TypedModule("TypedModule", new Version(1, 0, 0, null));  
    JavaType productlist = this.getTypeFactory().constructCollectionType(List.class, Product.class); 
    JavaType clientlist = this.getTypeFactory().constructCollectionType(List.class, Client.class); 
    simpleModule.addSerializer(productlist, new prodctlistserializer()); 
    simpleModule.addSerializer(clientlist, new clientlistserializer()); 
    this.registerModule(simpleModule); 

В результате поведение, когда «Список типа продукта» передается ObjectMapper , он пытается найти подходящий серилизатор. Мы сохраняем общие сериализаторы на карте _javaTypeMappings. Objecmapper выполняет findSerializer. Сначала findSerializer проверяет наличие доступного сериализатора в _javaTypeMappings. В этом случае он возвращает prodctlistserializer. Когда «List of type Client» передается, он возвращает clientlistserializer.

Я взял весь исходный код под com.fasterxml.jackson.databind.Module к пакету в моем проекте и добавил выше изменений.

3

Действительно ли вы хотите, чтобы сериализация/десериализация этого типа обрабатывалась по-разному, когда она находится внутри коллекции? Если нет, вы можете просто комментировать класс Product с чем-то вроде: @JsonSerialize(using = ProductSerializer.class), и Джексон будет использовать ваш сериализатор в любое время, когда он встретит Product (включая внутри коллекций).

Если вам действительно нужна различная обработка, когда внутри коллекции вы можете аннотировать класс с чем-то вроде: @JsonSerialize(using = ProductSerializer.class, contentUsing = ProductInCollectionSerializer.class). Это будет делать то же самое, что и выше, за исключением того, что ProductInCollectionSerializer будет использоваться для сериализации вместо этого, когда Джексон встречает коллекцию, содержащую Product.

EDIT на основе комментариев:

Джексон сериализовать коллекцию как массив JSON, так там нет места, чтобы поставить эти дополнительные свойства. Похоже, вы хотите, чтобы Джексон возвращал объект JSON, содержащий массив JSON, а также некоторые дополнительные метаданные. В этом случае, лучший подход обернуть List<Product> в объекте, таких как:

public class ProductListWrapper { 
    private final List<Product> products; 

    public ProductListWrapper(List<Product> products) { 
     this.products = products; 
    } 

    public List<Product> getProducts() { 
     return products; 
    } 

    @JsonProperty("_count") 
    public int getCount() { 
     return getProducts() == null ? 0 : getProducts().size(); 
    } 
} 

Джексон может затем сериализовать этот объект без необходимости писать какие-либо пользовательские сериализаторы и будет производить JSON выглядит как:

{ 
    "products": [....] 
    "_count": 25 
} 
+0

У меня есть бизнес-объект, который возвращает «Список » и другой, который возвращает список . Мне нужно, когда объект Busines возвращает «Список », тогда ProductListSerializer следует использовать для всего списка. и Когда бизнес-объект возвращает «Список », CustomClientsSerializer должен использоваться для всего списка. В моем случае использования я добавляю дополнительные элементы в сериализованный json 'eg: __ count, __metadata, __references'. В коллекции есть только один __count. Contentusing Может использоваться только для свойств. Какие-либо предложения ? –

+0

на основе вашего редактирования .. Я изучал пользовательский сериализатор, чтобы избежать обертки объекта. С объектами-оболочками модель домена требует изменения. У меня есть способ избежать загрязнения модели домена? –

+0

Сверху моей головы. Я не могу придумать простой способ сделать то, что вы хотите, не делая ничего экстремального, как добавление аннотаций микширования в List.class. Если вы не закроете список, где ваш сериализатор получит метаданные? (помимо таких вещей, как «_count», которые можно найти в списке), я предпочитаю как можно меньше настраивать представление JSON и использовать аннотации для настройки по возможности, чтобы люди могли смотреть на объект, который они получают, и знать, что JSON для ожидать. Если вы начнете писать сериализаторы для разных типов списков, как кто-то может знать, что ожидать от JSON? – HiJon89

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