加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 使用Jackson令牌流的JSON(> 1演出)中的长字符串

发布时间:2020-12-15 02:09:37 所属栏目:Java 来源:网络整理
导读:我正在尝试编写一些代码处理 JSON文档,其中包含存储在文件中的极长字符串值(超过10亿个字符).我不想将整个字符串保留在内存中(因为我可以在流中处理它们).但我在Jackson解析器中找不到这样的选项.到目前为止我所做的是使用Jackson令牌偏移(第一轮阅读文件)和
我正在尝试编写一些代码处理 JSON文档,其中包含存储在文件中的极长字符串值(超过10亿个字符).我不想将整个字符串保留在内存中(因为我可以在流中处理它们).但我在Jackson解析器中找不到这样的选项.到目前为止我所做的是使用Jackson令牌偏移(第一轮阅读文件)和随机访问文件来处理流中的字符串(第二轮阅读文件):

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.MappingJsonFactory;

public class LongStringJsonTest {
    public static void main(String[] args) throws Exception {
        File tempJson = new File("temp.json");
        PrintWriter pw = new PrintWriter(tempJson);
        pw.print("{"k1": {"k11": "");
        for (int i = 0; i < 1e8; i++)
            pw.print("abcdefghij"); 
        pw.print(""},"k2": "klmnopqrst"," +
                ""k3": ["uvwxyz","0123"]}");
        pw.close();
        searchForStrings(tempJson);
    }

    private static void searchForStrings(File tempJson) throws Exception {
        JsonFactory f = new MappingJsonFactory();
        JsonParser jp = f.createParser(tempJson);
        Map<Long,Long> stringStartToNext = new HashMap<Long,Long>();
        long lastStringStart = -1;
        boolean wasFieldBeforeString = false;
        while (true) {
            JsonToken token = jp.nextToken();
            if (token == null)
                break;
            if (lastStringStart >= 0) {
                stringStartToNext.put(lastStringStart,(wasFieldBeforeString ? -1 : 1) *
                        jp.getTokenLocation().getByteOffset());
                lastStringStart = -1;
                wasFieldBeforeString = false;
            }
            if (token == JsonToken.FIELD_NAME) {
                wasFieldBeforeString = true;
            } else if (token == JsonToken.VALUE_STRING) {
                lastStringStart = jp.getTokenLocation().getByteOffset();
            } else {
                wasFieldBeforeString = false;
            }
        }
        jp.close();
        jp = f.createParser(tempJson);
        RandomAccessFile raf = new RandomAccessFile(tempJson,"r");
        while (true) {
            JsonToken token = jp.nextToken();
            if (token == null)
                break;
            if (token == JsonToken.VALUE_STRING) {
                long start = jp.getTokenLocation().getByteOffset();
                long end = stringStartToNext.get(start);
                // You are able to process stream without keeping all bytes in memory.
                // Here you see strings including quotes around them.
                final long[] length = new long[] {0};
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                OutputStream os = new OutputStream() {
                    @Override
                    public void write(int b) throws IOException {
                        throw new IOException("Method is not supported");
                    }
                    @Override
                    public void write(byte[] b,int off,int len)
                            throws IOException {
                        if (baos.size() < 20) {
                            baos.write(b,off,Math.min(len,20));
                            baos.write((int)'.');
                            baos.write((int)'.');
                            baos.write((int)'.');
                        }
                        if (len > 0)
                            length[0] += len;
                    }
                };
                processString(raf,start,end,os);
                String text = new String(baos.toByteArray(),Charset.forName("utf-8"));
                System.out.println("String: " + text + ",length=" + length[0]);
            }
        }
        jp.close();
        raf.close();
    }

    private static void processString(RandomAccessFile raf,long start,long end,OutputStream os) throws Exception {
        boolean wasFieldBeforeString = end < 0;
        int quoteNum = wasFieldBeforeString ? 3 : 1;
        end = Math.abs(end);
        byte[] buffer = new byte[10000];
        raf.seek(start);
        boolean afterBackSlash = false;
        int strLen = (int)(end - start);
        for (int chunk = 0; strLen > 0; chunk++) {
            int ret = raf.read(buffer,Math.min(buffer.length,strLen));
            if (ret < 0)
                break;
            if (ret > 0) {
                int offset = 0;
                if (chunk == 0) {
                    // Assumption that key string doesn't contain double quotes 
                    // and it's shorter than buffer size (for simplicity)
                    for (int n = 0; n < quoteNum; n++) {
                        while (true) {
                            if (buffer[offset] == '"' && !afterBackSlash) {
                                break;
                            } else if (buffer[offset] == '') {
                                afterBackSlash = !afterBackSlash;
                            } else {
                                afterBackSlash = false;
                            }
                            offset++;
                        }
                        offset++;
                    }
                    offset--;
                    ret -= offset;
                }
                // Searching for ending quote
                int endQuotePos = offset + (chunk == 0 ? 1 : 0); // Skip open quote
                while (endQuotePos < offset + ret) {
                    if (buffer[endQuotePos] == '"' && !afterBackSlash) {
                        break;
                    } else if (buffer[endQuotePos] == '') {
                        afterBackSlash = !afterBackSlash;
                    } else {
                        afterBackSlash = false;
                    }
                    endQuotePos++;
                }
                if (endQuotePos < offset + ret) {
                    os.write(buffer,offset,endQuotePos + 1 - offset);
                    break;
                }
                os.write(buffer,ret);
                strLen -= ret;
            }
        }
    }
}

这种方法根本不支持unicode.我很好奇有没有办法做得更好(或者甚至在其他一些libs的帮助下)?

解决方法

我想你问的是错误的问题.

JSON,如XML或CSV,或任何其他结构化文本表示,有三个主要角色:使数据结构人为可解析,允许通用工具处理许多不同类型的数据,并促进可能使用不同内部的系统之间的数据交换楷模.

如果您不需要这些特定的特征,结构化文本可能是错误的解决方案.专用二进制表示可以更有效,并且随着数据的大小/复杂性增加,该差异可能变得巨大.

支持结构化文本格式,以便导入和导出工具.但是,在内部,您可能应该使用专门针对特定任务需求而调整的数据模型.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读