com.github.shyiko.jackson.module.advice.AdvisedBeanSerializer.java Source code

Java tutorial

Introduction

Here is the source code for com.github.shyiko.jackson.module.advice.AdvisedBeanSerializer.java

Source

/*
 * Copyright 2013 Stanley Shyiko
 *
 * 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 com.github.shyiko.jackson.module.advice;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerBuilder;
import com.fasterxml.jackson.databind.ser.PropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.BeanAsArraySerializer;
import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter;
import com.fasterxml.jackson.databind.ser.impl.WritableObjectId;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
import com.fasterxml.jackson.databind.util.NameTransformer;

import java.io.IOException;

/**
 * Whole class is basically a rip-off of {@link com.fasterxml.jackson.databind.ser.BeanSerializer} with inlined
 * for-{@link BeanSerializerAdvice} calls. Original code belongs to FasterXML, LLC (licensed under Apache License 2.0).
 * @author <a href="mailto:stanley.shyiko@gmail.com">Stanley Shyiko</a>
 */
@SuppressWarnings("unchecked")
public class AdvisedBeanSerializer extends BeanSerializerBase {

    private final BeanSerializerAdvice beanSerializerAdvice;
    private final boolean unwrappingSerializer;

    protected AdvisedBeanSerializer(JavaType type, BeanSerializerBuilder builder, BeanPropertyWriter[] properties,
            BeanPropertyWriter[] filteredProperties, BeanSerializerAdvice beanSerializerAdvice) {
        super(type, builder, properties, filteredProperties);
        this.beanSerializerAdvice = beanSerializerAdvice;
        this.unwrappingSerializer = false;
    }

    protected AdvisedBeanSerializer(AdvisedBeanSerializer src, String[] toIgnore) {
        super(src, toIgnore);
        this.beanSerializerAdvice = src.beanSerializerAdvice;
        this.unwrappingSerializer = src.unwrappingSerializer;
    }

    protected AdvisedBeanSerializer(AdvisedBeanSerializer src, ObjectIdWriter objectIdWriter, Object filterId) {
        super(src, objectIdWriter, filterId);
        this.beanSerializerAdvice = src.beanSerializerAdvice;
        this.unwrappingSerializer = src.unwrappingSerializer;
    }

    protected AdvisedBeanSerializer(AdvisedBeanSerializer src, NameTransformer nameTransformer,
            boolean unwrappingSerializer) {
        super(src, nameTransformer);
        this.beanSerializerAdvice = src.beanSerializerAdvice;
        this.unwrappingSerializer = unwrappingSerializer;
    }

    @Override
    public JsonSerializer<Object> unwrappingSerializer(NameTransformer nameTransformer) {
        return new AdvisedBeanSerializer(this, nameTransformer, true);
    }

    @Override
    public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) {
        return new AdvisedBeanSerializer(this, objectIdWriter, _propertyFilterId);
    }

    @Override
    protected BeanSerializerBase withFilterId(Object filterId) {
        return new AdvisedBeanSerializer(this, _objectIdWriter, filterId);
    }

    @Override
    protected BeanSerializerBase withIgnorals(String[] toIgnore) {
        return new AdvisedBeanSerializer(this, toIgnore);
    }

    @Override
    protected BeanSerializerBase asArraySerializer() {
        /* Can not:
         * - have Object Id (may be allowed in future)
         * - have any getter
         */
        if ((_objectIdWriter == null) && (_anyGetterWriter == null) && (_propertyFilterId == null)) {
            return new BeanAsArraySerializer(this);
        }
        // already is one, so:
        return this;
    }

    @Override
    public void serialize(Object bean, JsonGenerator jgen, SerializerProvider provider) throws IOException {
        if (_objectIdWriter != null) {
            serializeWithObjectId(bean, jgen, provider, !unwrappingSerializer);
            return;
        }
        if (!unwrappingSerializer) {
            jgen.writeStartObject();
        }
        if (!beanSerializerAdvice.intercept(bean, jgen, provider)) {
            beanSerializerAdvice.before(bean, jgen, provider);
            if (_propertyFilterId != null) {
                serializeFieldsFiltered(bean, jgen, provider);
            } else {
                serializeFields(bean, jgen, provider);
            }
            beanSerializerAdvice.after(bean, jgen, provider);
        }
        if (!unwrappingSerializer) {
            jgen.writeEndObject();
        }
    }

    protected void serializeWithObjectId(Object bean, JsonGenerator jgen, SerializerProvider provider,
            boolean startEndObject) throws IOException {
        final ObjectIdWriter w = _objectIdWriter;
        WritableObjectId objectId = provider.findObjectId(bean, w.generator);
        // If possible, write as id already
        if (objectId.writeAsId(jgen, provider, w)) {
            return;
        }
        // If not, need to inject the id:
        Object id = objectId.generateId(bean);
        if (w.alwaysAsId) {
            w.serializer.serialize(id, jgen, provider);
            return;
        }
        if (startEndObject) {
            jgen.writeStartObject();
        }
        if (!beanSerializerAdvice.intercept(bean, jgen, provider)) {
            beanSerializerAdvice.before(bean, jgen, provider);
            objectId.writeAsField(jgen, provider, w);
            if (_propertyFilterId != null) {
                serializeFieldsFiltered(bean, jgen, provider);
            } else {
                serializeFields(bean, jgen, provider);
            }
            beanSerializerAdvice.after(bean, jgen, provider);
        }
        if (startEndObject) {
            jgen.writeEndObject();
        }
    }

    protected void serializeFieldsFiltered(Object bean, JsonGenerator jgen, SerializerProvider provider)
            throws IOException {
        /* note: almost verbatim copy of "serializeFields"; copied (instead of merged)
         * so that old method need not add check for existence of filter.
         */

        final BeanPropertyWriter[] props;
        if (_filteredProps != null && provider.getActiveView() != null) {
            props = _filteredProps;
        } else {
            props = _props;
        }
        final PropertyFilter filter = findPropertyFilter(provider, _propertyFilterId, bean);
        // better also allow missing filter actually..
        if (filter == null) {
            serializeFields(bean, jgen, provider);
            return;
        }
        int i = 0;
        try {
            for (final int len = props.length; i < len; ++i) {
                BeanPropertyWriter prop = props[i];
                if (prop != null) { // can have nulls in filtered list
                    if (!beanSerializerAdvice.intercept(bean, jgen, prop, provider)) {
                        beanSerializerAdvice.before(bean, jgen, prop, provider);
                        filter.serializeAsField(bean, jgen, provider, prop);
                        beanSerializerAdvice.after(bean, jgen, prop, provider);
                    }
                }
            }
            if (_anyGetterWriter != null) {
                _anyGetterWriter.getAndFilter(bean, jgen, provider, filter);
            }
        } catch (Exception e) {
            String name = (i == props.length) ? "[anySetter]" : props[i].getName();
            wrapAndThrow(provider, e, bean, name);
        } catch (StackOverflowError e) {
            JsonMappingException mapE = new JsonMappingException("Infinite recursion (StackOverflowError)", e);
            String name = (i == props.length) ? "[anySetter]" : props[i].getName();
            mapE.prependPath(new JsonMappingException.Reference(bean, name));
            throw mapE;
        }
    }

    protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider)
            throws IOException {
        final BeanPropertyWriter[] props;
        if (_filteredProps != null && provider.getActiveView() != null) {
            props = _filteredProps;
        } else {
            props = _props;
        }
        int i = 0;
        try {
            for (final int len = props.length; i < len; ++i) {
                BeanPropertyWriter prop = props[i];
                if (prop != null) { // can have nulls in filtered list
                    if (!beanSerializerAdvice.intercept(bean, jgen, prop, provider)) {
                        beanSerializerAdvice.before(bean, jgen, prop, provider);
                        prop.serializeAsField(bean, jgen, provider);
                        beanSerializerAdvice.after(bean, jgen, prop, provider);
                    }
                }
            }
            if (_anyGetterWriter != null) {
                _anyGetterWriter.getAndSerialize(bean, jgen, provider);
            }
        } catch (Exception e) {
            String name = (i == props.length) ? "[anySetter]" : props[i].getName();
            wrapAndThrow(provider, e, bean, name);
        } catch (StackOverflowError e) {
            /* 04-Sep-2009, tatu: Dealing with this is tricky, since we do not
             *   have many stack frames to spare... just one or two; can't
             *   make many calls.
             */
            JsonMappingException mapE = new JsonMappingException("Infinite recursion (StackOverflowError)", e);
            String name = (i == props.length) ? "[anySetter]" : props[i].getName();
            mapE.prependPath(new JsonMappingException.Reference(bean, name));
            throw mapE;
        }
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + " for " + handledType().getName();
    }

}