如何在Jackson中逐步读取文件中的json序列?

时间:2014-12-17 23:11:20

标签: java json jackson

一个过程是创建一个json文档序列的日志。另一个进程想要在生成日志时读取它(类似于tail -f)并使用Jackson JsonParser(和ObjectReader)解析它。当Reader.read暂时返回-1时,你如何要求JsonParser或MappingIterator不关闭?

尝试通过EOF经常在Reader上创建JsonReader.readValues的示例:

import static org.junit.Assert.*;

import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import org.junit.Test;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;

public class ParitalParserTest {
    /** Reader that temporarily returns -1 (EOF) to simulate growing file */
    private static class StringReaderWithEofs extends Reader {
        public final String string;
        private int pos;
        private int limit;
        public StringReaderWithEofs(String string) {
            this.string = string;
        }
        public void limit(int limit) { 
            this.limit = limit;
        }
        @Override public int read(char[] cbuf, int off, int len) throws IOException {
            int newPos = Math.min(pos + len, limit);
            string.getChars(pos, newPos, cbuf, off);
            int r = newPos - this.pos;
            if (r == 0) r = -1;
            this.pos = newPos;
            return r;
        }
        @Override public void close() throws IOException {}
    }
    /** POJO log event */
    public static class LogEvent {
        private final String message;
        @JsonCreator
        public LogEvent(@JsonProperty("message") String message) {
            this.message = message;
        }
        public String getMessage() {
            return message;
        }
        @Override public int hashCode() {
            return Objects.hash(message);
        }
        @Override public boolean equals(Object obj) {
            if (! (obj instanceof LogEvent) || obj.getClass() != this.getClass()) return false;
            LogEvent o = (LogEvent) obj;
            return Objects.equals(message, o.message);
        }
        @Override public String toString() {
            return String.format("%s{message=%s}", getClass().getSimpleName(), message);
        }

    }
    public void test(boolean withEofs) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        ObjectReader jsonReader = mapper.reader(LogEvent.class);
        String one = "{\"message\":\"one\"}\n";
        String two = "{\"message\":\"two\"}\n";
        String three = "{\"message\":\"three\"}\n";
        StringReaderWithEofs reader = new StringReaderWithEofs(one + two + three);
        MappingIterator<LogEvent> it = null;
        List<LogEvent> values = new ArrayList<>();
        int firstLimit = withEofs ? 1 : reader.string.length();
        int lastLimit = reader.string.length();
        for (int i = firstLimit; i <= lastLimit; i++) {
            reader.limit(i);
            if (it == null) {
                it = jsonReader.readValues(reader);
            }
            while (it.hasNext()) {
                LogEvent value = it.next();
                values.add(value);
            }
        }
        List<LogEvent> expected = Arrays.asList(new LogEvent("one"), new LogEvent("two"), new LogEvent("three"));
        assertEquals(expected, values);
    }
    @Test public void testReadFullFile() throws IOException {
        test(false);
    }
    @Test public void testReadPartialFile() throws IOException {
        test(true);
    }

}

1 个答案:

答案 0 :(得分:3)

这有点棘手,但我可能会尝试创建一个Reader,如果没有数据可用,实际阻塞,睡眠,检查等等。这样杰克逊就不必担心缺乏内容。

ReaderInputStream返回0实际上并没有帮助,因为解析器无法做很多事情,因为实际上没有办法正确阻止。或者,换句话说:底层读者或流必须尝试读取至少一个字节(如果一个或多个请求),如果没有找到则阻塞;如果没有可用的话,则返回-1。