You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JSON转字符串存库及反向转换的优化方案咨询

问题需求

需将如下结构的JSON转换为字符串字段storedInput存入数据库,同时支持把该字符串反向还原为原JSON格式:

{
"students":[{
"name": "Albert",
"code":"GE",
"marks":[{"mark":"20"},{"mark":"40"}]},
{
"name": "Gert",
"code":"LE",
"marks":[{"mark":"26"}]},
{
"name": "John"
},
{
"name": "John Doe",
"code":"LP",
"marks":[{"mark":"40"}]}
]
}

原方案采用分隔符拼接字符串(示例:"Albert-GE-20&40#Gert-LE-26#John-$-$#John Doe-LP-40"),但反向解析时极易出错,对应的Java转换代码如下:

public String convertStudentList(List<Student> studentList) {
    return studentList.stream().map(this::mapStudent).collect(Collectors.joining("#"));
}

public String checkData(String data) {
    return Optional.ofNullable(data).isPresent() ? data : "$";
}

public String mapStudent(Student student) {
    List<Marks> marks = student.getMarks();
    if (marks != null) {
        String mark = marks.stream().map(m -> m.getMark()).collect(Collectors.joining("&"));
        return checkData(student.getName()) + "-" + checkData(student.getCode()) + "-" + mark;
    } else {
        return checkData(student.getName()) + "-" + checkData(student.getCode()) + "-" + "$";
    }
}

约束条件

  • 无法修改数据库表结构
  • 受存储空间限制,不能直接存储完整JSON(如用Jackson ObjectMapper序列化的结果),必须仅存储有效数据且能完整还原

可行解决方案

针对原方案反解析困难的问题,推荐带转义的分隔符方案,核心是对包含分隔符的字段内容做转义处理,同时明确字段拆分规则,确保反向解析时能准确还原数据。

核心设计规则

  1. 定义分隔符与转义符:

    • 学生条目之间用#分隔
    • 单个学生的name、code、marks组之间用|分隔(避免和姓名/成绩中的-冲突)
    • 同一学生的多个成绩用&分隔
    • 转义符用\:如果字段内容中出现#、|、&、\,则在字符前添加\做转义
  2. 序列化(转字符串)逻辑:

    • 遍历每个学生,对name、code做转义处理
    • 若marks非空,将所有mark值转义后用&拼接;为空则用$占位
    • 用|拼接转义后的name、code、成绩字符串
    • 用#拼接所有学生的字符串
  3. 反序列化(还原JSON)逻辑:

    • 拆分字符串时跳过转义后的特殊字符(如\#不视为学生分隔符)
    • 拆分出的字段内容再做反转义,还原原始内容
    • 根据占位符$判断是否为空值,进而还原对应的对象结构

示例转换结果

原JSON对应的序列化字符串:

Albert|GE|20&40#Gert|LE|26#John|$|$#John Doe|LP|40

若有学生姓名为John#Doe,转义后会变为John\#Doe,避免拆分时误判为学生分隔符。

Java代码实现示例

序列化工具方法

private static final String STUDENT_SEP = "#";
private static final String FIELD_SEP = "|";
private static final String MARK_SEP = "&";
private static final String ESCAPE = "\\";
private static final String NULL_PLACEHOLDER = "$";

private String escape(String input) {
    if (input == null) return NULL_PLACEHOLDER;
    return input.replace(ESCAPE, ESCAPE + ESCAPE)
                .replace(STUDENT_SEP, ESCAPE + STUDENT_SEP)
                .replace(FIELD_SEP, ESCAPE + FIELD_SEP)
                .replace(MARK_SEP, ESCAPE + MARK_SEP);
}

public String convertStudentList(List<Student> studentList) {
    return studentList.stream().map(this::mapStudent).collect(Collectors.joining(STUDENT_SEP));
}

public String mapStudent(Student student) {
    String escapedName = escape(student.getName());
    String escapedCode = escape(student.getCode());
    String marksStr;
    if (student.getMarks() != null && !student.getMarks().isEmpty()) {
        marksStr = student.getMarks().stream()
                          .map(m -> escape(m.getMark()))
                          .collect(Collectors.joining(MARK_SEP));
    } else {
        marksStr = NULL_PLACEHOLDER;
    }
    return String.join(FIELD_SEP, escapedName, escapedCode, marksStr);
}

反序列化工具方法

private String unescape(String input) {
    if (NULL_PLACEHOLDER.equals(input)) return null;
    StringBuilder sb = new StringBuilder();
    boolean escaped = false;
    for (char c : input.toCharArray()) {
        if (escaped) {
            sb.append(c);
            escaped = false;
        } else if (ESCAPE.charAt(0) == c) {
            escaped = true;
        } else {
            sb.append(c);
        }
    }
    return sb.toString();
}

public List<Student> parseStudentList(String storedInput) {
    if (storedInput == null || storedInput.isEmpty()) return Collections.emptyList();
    
    List<Student> students = new ArrayList<>();
    // 手动拆分学生条目,处理转义的#
    List<String> studentStrs = new ArrayList<>();
    StringBuilder current = new StringBuilder();
    boolean escaped = false;
    for (char c : storedInput.toCharArray()) {
        if (escaped) {
            current.append(c);
            escaped = false;
        } else if (ESCAPE.charAt(0) == c) {
            escaped = true;
        } else if (STUDENT_SEP.charAt(0) == c) {
            studentStrs.add(current.toString());
            current.setLength(0);
        } else {
            current.append(c);
        }
    }
    if (current.length() > 0) {
        studentStrs.add(current.toString());
    }
    
    for (String studentStr : studentStrs) {
        // 拆分学生字段,处理转义的|
        List<String> fields = new ArrayList<>();
        current.setLength(0);
        escaped = false;
        for (char c : studentStr.toCharArray()) {
            if (escaped) {
                current.append(c);
                escaped = false;
            } else if (ESCAPE.charAt(0) == c) {
                escaped = true;
            } else if (FIELD_SEP.charAt(0) == c) {
                fields.add(current.toString());
                current.setLength(0);
            } else {
                current.append(c);
            }
        }
        if (current.length() > 0) {
            fields.add(current.toString());
        }
        
        Student student = new Student();
        student.setName(unescape(fields.get(0)));
        student.setCode(unescape(fields.get(1)));
        
        String marksField = fields.get(2);
        if (!NULL_PLACEHOLDER.equals(marksField)) {
            List<Marks> marks = new ArrayList<>();
            // 拆分成绩,处理转义的&
            List<String> markStrs = new ArrayList<>();
            current.setLength(0);
            escaped = false;
            for (char c : marksField.toCharArray()) {
                if (escaped) {
                    current.append(c);
                    escaped = false;
                } else if (ESCAPE.charAt(0) == c) {
                    escaped = true;
                } else if (MARK_SEP.charAt(0) == c) {
                    markStrs.add(current.toString());
                    current.setLength(0);
                } else {
                    current.append(c);
                }
            }
            if (current.length() > 0) {
                markStrs.add(current.toString());
            }
            
            for (String markStr : markStrs) {
                Marks mark = new Marks();
                mark.setMark(unescape(markStr));
                marks.add(mark);
            }
            student.setMarks(marks);
        } else {
            student.setMarks(null);
        }
        students.add(student);
    }
    return students;
}

方案优势

  • 解决了原方案中字段含分隔符导致的解析错误问题,反向还原逻辑可靠
  • 仅存储有效数据,空间占用和原方案相当,满足存储限制
  • 无需修改数据库表结构,直接替换原有转换逻辑即可落地

内容的提问来源于stack exchange,提问作者kselvan9000

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 11:50:45