java—当对象足够大时,如何告诉jackson序列化程序停止继续序列化和写入

mqxuamgl  于 2021-07-06  发布在  Java
关注(0)|答案(2)|浏览(417)

在我最近的项目中,有很多日志语句如下:

Bar bar = foo();
logger.info("something happened: param:{}", JSON.toJSONString(bar));

如您所见,只需json将对象序列化到日志中。有时bar非常大,序列化bar对象会占用太多时间,日志文件会扩展很多。json序列化框架是fasterxml
所以我的问题是:有没有一种方法,比如定制jsonserializer来实现:
计算已序列化的字符/字节数(通过调用方法#tojsonstring())
如果计数超过了最大值,比如说2000,那么停止
只返回2000字符的字符串并在日志文件中打印它

5q4ezhmt

5q4ezhmt1#

在做了一些研究之后,下面的想法出现了。这不是很优雅。希望有人能改进这个!

import java.io.IOException;
import java.io.Writer;
import java.util.stream.IntStream;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.json.WriterBasedJsonGenerator;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.MappingJsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.SerializerFactory;
import com.fasterxml.jackson.databind.ser.Serializers;
import org.jetbrains.annotations.NotNull;

public class JSON {

    static int MAX_SIZE = 10;
    final static ObjectMapper OBJECT_MAPPER;
    static {
        OBJECT_MAPPER = new ObjectMapper(new WrappedJsonFactory(new MappingJsonFactory()), null, null);
        OBJECT_MAPPER.setSerializerFactory(new WrapperSerialFactory(OBJECT_MAPPER.getSerializerFactory()));
    }

    static String toJSONString(Object value) {
        try {
            return OBJECT_MAPPER.writeValueAsString(value);
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void main(String... args) throws Exception {
        int[] array = IntStream.range(1, 100).toArray();
        System.out.println(JSON.toJSONString(array));
        MAX_SIZE = 20;
        System.out.println(JSON.toJSONString(array));
        MAX_SIZE = 30;
        System.out.println(JSON.toJSONString(array));
    }

    static class WrapperSerialFactory extends SerializerFactory {

        private SerializerFactory wrapped;

        public WrapperSerialFactory(SerializerFactory wrapped) {
            this.wrapped = wrapped;
        }

        @Override
        public SerializerFactory withAdditionalSerializers(Serializers additional) {
            return wrapped.withAdditionalSerializers(additional);
        }

        @Override
        public SerializerFactory withAdditionalKeySerializers(Serializers additional) {
            return wrapped.withAdditionalKeySerializers(additional);
        }

        @Override
        public SerializerFactory withSerializerModifier(BeanSerializerModifier modifier) {
            return wrapped.withSerializerModifier(modifier);
        }

        @Override
        public JsonSerializer<Object> createSerializer(SerializerProvider prov, JavaType baseType)
            throws JsonMappingException {
            JsonSerializer<Object> serializer = wrapped.createSerializer(prov, baseType);
            return new WrappedTypeSerializer(serializer);
        }

        @Override
        public TypeSerializer createTypeSerializer(SerializationConfig config, JavaType baseType)
            throws JsonMappingException {
            return wrapped.createTypeSerializer(config, baseType);
        }

        @Override
        public JsonSerializer<Object> createKeySerializer(SerializationConfig config, JavaType type,
            JsonSerializer<Object> defaultImpl) throws JsonMappingException {
            return wrapped.createKeySerializer(config, type, defaultImpl);
        }
    }

    static class WrappedJsonFactory extends JsonFactory {
        private JsonFactory wrapped;

        public WrappedJsonFactory(JsonFactory wrapped) {
            this.wrapped = wrapped;
        }

        @Override
        public JsonGenerator createGenerator(Writer w) throws IOException {
            w = new LimitingWriter(w);
            return wrapped.createGenerator(w);
        }
    }

    static class LimitingWriter extends Writer {
        private int count;
        private boolean finished = false;

        private Writer delegate;

        public LimitingWriter(Writer delegate) {
            this.delegate = delegate;
        }

        private void finish() throws IOException {
            if (!finished) {
                delegate.write("...");
                finished = true;
            }
        }

        @Override
        public void write(int c) throws IOException {
            if (!finished) {
                if (count >= MAX_SIZE) {
                    finish();
                } else {
                    delegate.write(c);
                    ++count;
                }
            }
        }

        @Override
        public void write(@NotNull char[] cbuf) throws IOException {
            if (!finished) {
                if ((count + cbuf.length) > MAX_SIZE) {
                    delegate.write(cbuf, 0, MAX_SIZE - count);
                    count = MAX_SIZE;
                    finish();
                } else {
                    delegate.write(cbuf);
                    count += cbuf.length;
                }
            }
        }

        @Override
        public void write(@NotNull String str) throws IOException {
            if (!finished) {
                if (count + str.length() > MAX_SIZE) {
                    delegate.write(str, 0, MAX_SIZE - count);
                    count = MAX_SIZE;
                    finish();
                } else {
                    delegate.write(str);
                    count += str.length();
                }
            }
        }

        @Override
        public void write(@NotNull String str, int off, int len) throws IOException {
            if (!finished) {
                if (count + len > MAX_SIZE) {
                    delegate.write(str, 0, MAX_SIZE - count);
                    count = MAX_SIZE;
                    finish();
                } else {
                    delegate.write(str, off, len);
                    count += len;
                }
            }
        }

        @Override
        public Writer append(CharSequence csq) throws IOException {
            if (!finished) {
                if (count + csq.length() > MAX_SIZE) {
                    count = MAX_SIZE;
                    delegate.append(csq, 0, MAX_SIZE - count);
                    finish();
                } else {
                    count += csq.length();
                    delegate.append(csq);
                }
            }
            return this;
        }

        @Override
        public Writer append(CharSequence csq, int start, int end) throws IOException {
            if (!finished) {
                if (count + end - start > MAX_SIZE) {
                    delegate.append(csq, start, start + MAX_SIZE - count);
                    count = MAX_SIZE;
                    finish();
                } else {
                    count += (end - start);
                    delegate.append(csq, start, end);
                }
            }
            return this;
        }

        @Override
        public Writer append(char c) throws IOException {
            if (!finished) {
                if (count >= MAX_SIZE) {
                    finish();
                } else {
                    ++count;
                    delegate.append(c);
                }
            }
            return this;
        }

        @Override
        public void write(@NotNull char[] cbuf, int off, int len) throws IOException {
            if (!finished) {
                if (count + len > MAX_SIZE) {
                    delegate.write(cbuf, off, MAX_SIZE - count);
                    count = MAX_SIZE;
                    finish();
                } else {
                    delegate.write(cbuf, off, len);
                    count += len;
                }
            }
        }

        @Override
        public void flush() throws IOException {
            delegate.flush();
        }

        @Override
        public void close() throws IOException {
            delegate.close();
        }
    }

    static class WrappedTypeSerializer extends JsonSerializer {
        private JsonSerializer wrapped;

        public WrappedTypeSerializer(JsonSerializer wrapped) {
            this.wrapped = wrapped;
        }

        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            WriterBasedJsonGenerator writerBasedJsonGenerator = (WriterBasedJsonGenerator)gen;
            LimitingWriter writer = (LimitingWriter)writerBasedJsonGenerator.getOutputTarget();
            if (writer.finished) {
                return;
            }
            wrapped.serialize(value, gen, serializers);
        }
    }
}

输出:

[1,2,3,4,5...
[1,2,3,4,5,6,7,8,9,1...
[1,2,3,4,5,6,7,8,9,10,11,12,13...
brqmpdu1

brqmpdu12#

如果我们不想陷入 Jackson 我们可以限制 Writer 将对象序列化为。这是一种简单的方法,不需要在内部检查限制的情况下实现自定义序列化程序。这种方法的缺点是我们并没有跳过序列化过程,只是忽略了它的结果。
你可以自己写 Writer 具有最大缓冲区大小。基于 StringBuilder :

class LimitedStringBuilderWriter extends Writer {

    private final StringBuilder buffer;
    private int remaining;

    public LimitedStringBuilderWriter(int limit) {
        if (limit <= 0) {
            throw new IllegalArgumentException("Limit must be positive number!");
        }

        this.remaining = limit;
        this.buffer = new StringBuilder(limit);
    }

    @Override
    public void write(char[] cbuf, int off, int len) {
        if (len == 0 || this.remaining <= 0) {
            return;
        }
        if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        }
        final int size = len - off;
        if (this.remaining >= size) {
            this.remaining -= size;
            buffer.append(cbuf, off, len);
            return;
        }
        buffer.append(cbuf, off, this.remaining);
        this.remaining = 0;
    }

    @Override
    public void write(int c) {
        if (this.remaining <= 0) {
            return;
        }
        this.remaining--;
        this.buffer.append(c);
    }

    @Override
    public void flush() {
    }

    @Override
    public void close() {
    }

    @Override
    public String toString() {
        return this.buffer.toString();
    }
}

用法:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;

import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Supplier;

public class LimitJsonLogApp {

    public static void main(String[] args) {
        Map<String, Object> value = new LinkedHashMap<>();
        value.put("array", Arrays.asList(1, 2, 3));
        value.put("string", "Value");
        value.put("int", 23);

        for (int limit = 11; limit < 30; limit += 3) {
            System.out.println(limit + " => " + JSON.toJSONString(value, limit));
        }
    }
}

class JSON {

    private static final ObjectMapper mapper = JsonMapper.builder().build();

    public static String toJSONString(Object value) {
        return toJSONStringSupplier(value).get();
    }

    public static String toJSONString(Object value, int limit) {
        return toJSONStringSupplier(value, limit).get();
    }

    public static Supplier<String> toJSONStringSupplier(Object value) {
        return toJSONStringSupplier(value, 1000);
    }

    public static Supplier<String> toJSONStringSupplier(Object value, int limit) {
        if (value == null) {
            return () -> "null";
        }
        return () -> {
            try {
                LimitedStringBuilderWriter writer = new LimitedStringBuilderWriter(limit);
                mapper.writeValue(writer, value);
                return writer.toString();
            } catch (IOException e) {
                throw new IllegalArgumentException(e);
            }
        };
    }
}

以上代码打印:

11 => {"array":[1
14 => {"array":[1,2,
17 => {"array":[1,2,3],
20 => {"array":[1,2,3],"st
23 => {"array":[1,2,3],"strin
26 => {"array":[1,2,3],"string":
29 => {"array":[1,2,3],"string":"Va

注意,有些方法 JSON 类返回 Supplier<String> . 如果您的记录器允许提供供应商,您应该使用它们。它允许将序列化推迟到真正需要的时候。如果你不能 INFO 登录配置它将真正禁用此代码的执行。

相关问题