Fastjson2StringSerializer.java

package com.alibaba.fastjson2.support.solon;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONFactory;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.reader.ObjectReaderProvider;
import com.alibaba.fastjson2.writer.ObjectWriter;
import com.alibaba.fastjson2.writer.ObjectWriterProvider;
import org.noear.solon.Utils;
import org.noear.solon.core.convert.Converter;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.ModelAndView;
import org.noear.solon.core.util.Assert;
import org.noear.solon.core.util.MimeType;
import org.noear.solon.lang.Nullable;
import org.noear.solon.serialization.EntityStringSerializer;
import org.noear.solon.serialization.prop.JsonProps;
import org.noear.solon.serialization.prop.JsonPropsUtil2;

import java.io.IOException;
import java.lang.reflect.Type;

/**
 * Fastjson2 ������������������
 *
 * @author noear
 * @author ������������
 * @since 1.10
 * @since 2.8
 */
public class Fastjson2StringSerializer
        implements EntityStringSerializer {
    private static final String label = "/json";
    private static final Fastjson2StringSerializer _default = new Fastjson2StringSerializer();

    public static Fastjson2StringSerializer getDefault() {
        return _default;
    }

    private Fastjson2Decl<JSONWriter.Context, JSONWriter.Feature> serializeConfig;
    private Fastjson2Decl<JSONReader.Context, JSONReader.Feature> deserializeConfig;

    public Fastjson2StringSerializer(JsonProps jsonProps) {
        loadJsonProps(jsonProps);
    }

    public Fastjson2StringSerializer() { }

    /**
     * ���������������������
     */
    public Fastjson2Decl<JSONWriter.Context, JSONWriter.Feature> getSerializeConfig() {
        if (serializeConfig == null) {
            serializeConfig = new Fastjson2Decl<>(new JSONWriter.Context(new ObjectWriterProvider()));
        }

        return serializeConfig;
    }

    /**
     * ������������������������
     */
    public Fastjson2Decl<JSONReader.Context, JSONReader.Feature> getDeserializeConfig() {
        if (deserializeConfig == null) {
            deserializeConfig = new Fastjson2Decl<>(new JSONReader.Context(new ObjectReaderProvider()));
        }
        return deserializeConfig;
    }

    /**
     * ������������
     */
    @Override
    public String mimeType() {
        return "application/json";
    }

    /**
     * ������������
     *
     */
    @Override
    public Class<String> dataType() {
        return String.class;
    }

    /**
     * ������������
     *
     * @param ctx ���������������
     * @param mime ������������
     */
    @Override
    public boolean matched(Context ctx, String mime) {
        if (mime == null) {
            return false;
        } else {
            return mime.contains(label) || mime.startsWith(MimeType.APPLICATION_X_NDJSON_VALUE);
        }
    }

    /**
     * ������������������
     */
    @Override
    public String name() {
        return "fastjson2-json";
    }

    /**
     * ���������
     *
     * @param obj ������
     */
    @Override
    public String serialize(Object obj) throws IOException {
        return JSON.toJSONString(obj, getSerializeConfig().getContext());
    }

    /**
     * ������������
     *
     * @param data ������
     * @param toType ������������
     */
    @Override
    public Object deserialize(String data, Type toType) throws IOException {
        if (toType == null) {
            return JSON.parse(data, getDeserializeConfig().getContext());
        } else {
            return JSON.parseObject(data, toType, getDeserializeConfig().getContext());
        }
    }

    /**
     * ���������������
     *
     * @param ctx ���������������
     * @param data ������
     */
    @Override
    public void serializeToBody(Context ctx, Object data) throws IOException {
        //������������������������������������ //��� ndjson,sse ��������������� mime������������������������
        if (ctx.contentTypeNew() == null) {
            ctx.contentType(this.mimeType());
        }

        if (data instanceof ModelAndView) {
            ctx.output(serialize(((ModelAndView) data).model()));
        } else {
            ctx.output(serialize(data));
        }
    }

    /**
     * ������������������
     *
     * @param ctx ���������������
     */
    @Override
    public Object deserializeFromBody(Context ctx, @Nullable Type bodyType) throws IOException {
        String data = ctx.bodyNew();

        if (Utils.isNotEmpty(data)) {
            return JSON.parse(data, getDeserializeConfig().getContext());
        } else {
            return null;
        }
    }

    /**
     * ���������������
     *
     * @param clz ������
     * @param encoder ���������
     */
    public <T> void addEncoder(Class<T> clz, ObjectWriter encoder) {
        getSerializeConfig().getContext().getProvider().register(clz, encoder);
    }

    /**
     * ������������������������������������������
     *
     * @param clz ������
     * @param converter ���������
     */
    @Override
    public <T> void addEncoder(Class<T> clz, Converter<T, Object> converter) {
        addEncoder(clz, (out, obj, fieldName, fieldType, features) -> {
            Object val = converter.convert((T) obj);
            if (val == null) {
                out.writeNull();
            } else if (val instanceof String) {
                out.writeString((String) val);
            } else if (val instanceof Number) {
                if (val instanceof Long) {
                    out.writeInt64(((Number) val).longValue());
                } else if (val instanceof Integer) {
                    out.writeInt32(((Number) val).intValue());
                } else if (val instanceof Float) {
                    out.writeDouble(((Number) val).floatValue());
                } else {
                    out.writeDouble(((Number) val).doubleValue());
                }
            } else {
                throw new IllegalArgumentException("The result type of the converter is not supported: " + val.getClass().getName());
            }
        });
    }

    protected void loadJsonProps(JsonProps jsonProps) {
        if (jsonProps != null) {
            if (jsonProps.dateAsTicks) {
                jsonProps.dateAsTicks = false;
                getSerializeConfig().getContext().setDateFormat("millis");
            }

            if (Utils.isNotEmpty(jsonProps.dateAsFormat)) {
                //������������������������������������������������������������������������//������������������������������������
                getSerializeConfig().getContext().setDateFormat(jsonProps.dateAsFormat);
            }

            //JsonPropsUtil2.dateAsFormat(this, jsonProps);
            JsonPropsUtil2.dateAsTicks(this, jsonProps);
            JsonPropsUtil2.boolAsInt(this, jsonProps);

            boolean writeNulls = jsonProps.nullAsWriteable ||
                    jsonProps.nullNumberAsZero ||
                    jsonProps.nullArrayAsEmpty ||
                    jsonProps.nullBoolAsFalse ||
                    jsonProps.nullStringAsEmpty;

            if (jsonProps.nullStringAsEmpty) {
                getSerializeConfig().addFeatures(JSONWriter.Feature.WriteNullStringAsEmpty);
            }

            if (jsonProps.nullBoolAsFalse) {
                getSerializeConfig().addFeatures(JSONWriter.Feature.WriteNullBooleanAsFalse);
            }

            if (jsonProps.nullNumberAsZero) {
                getSerializeConfig().addFeatures(JSONWriter.Feature.WriteNullNumberAsZero);
            }

            if (jsonProps.boolAsInt) {
                getSerializeConfig().addFeatures(JSONWriter.Feature.WriteBooleanAsNumber);
            }

            if (jsonProps.longAsString) {
                getSerializeConfig().addFeatures(JSONWriter.Feature.WriteLongAsString);
            }

            if (jsonProps.nullArrayAsEmpty) {
                getSerializeConfig().addFeatures(JSONWriter.Feature.WriteNullListAsEmpty);
            }

            if (jsonProps.enumAsName) {
                getSerializeConfig().addFeatures(JSONWriter.Feature.WriteEnumsUsingName);
            }

            if (writeNulls) {
                getSerializeConfig().addFeatures(JSONWriter.Feature.WriteNulls);
            }
        }
    }

    public static class Fastjson2Decl<C, F> {
        private final boolean forSerialize;
        private C context;

        public Fastjson2Decl(C context) {
            this.context = context;

            if (context instanceof JSONWriter.Context) {
                forSerialize = true;
            } else {
                forSerialize = false;
            }
        }

        public C getContext() {
            return context;
        }

        public void setContext(C context) {
            Assert.notNull(context, "context can not be null");
            this.context = context;
        }

        public void setFeatures(F... features) {
            if (forSerialize) {
                ((JSONWriter.Context) context).setFeatures(JSONFactory.getDefaultWriterFeatures());
            } else {
                ((JSONReader.Context) context).setFeatures(JSONFactory.getDefaultReaderFeatures());
            }

            addFeatures(features);
        }

        public void addFeatures(F... features) {
            if (forSerialize) {
                //���������
                for (F f1 : features) {
                    JSONWriter.Feature feature = (JSONWriter.Feature) f1;
                    ((JSONWriter.Context) context).config(feature, true);
                }
            } else {
                for (F f1 : features) {
                    JSONReader.Feature feature = (JSONReader.Feature) f1;
                    ((JSONReader.Context) context).config(feature, true);
                }
            }
        }

        public void removeFeatures(F... features) {
            if (forSerialize) {
                //���������
                for (F f1 : features) {
                    JSONWriter.Feature feature = (JSONWriter.Feature) f1;
                    ((JSONWriter.Context) context).config(feature, false);
                }
            } else {
                for (F f1 : features) {
                    JSONReader.Feature feature = (JSONReader.Feature) f1;
                    ((JSONReader.Context) context).config(feature, false);
                }
            }
        }
    }
}