如何将Azure Search返回字符串转换为SearchDocument列表
问题说明
你获取到的字符串是Azure Search中SearchDocument类型调用默认toString()方法生成的输出,不属于标准JSON格式:
- 键名没有双引号包裹
- 键和值之间用
=分隔而非JSON要求的:
因此无法直接用Jackson默认配置反序列化,简单按逗号做字符串分割的方案,也无法识别字段值内部自带的逗号,会出现解析错位。
解决方案
方案1:从数据源层面避免解析字符串(推荐)
如果是你通过Azure Search Java SDK原生接口拿到的搜索结果,完全不需要做字符串解析:
- 拿到根
SearchDocument对象后,直接调用get("你的复杂集合字段名")方法,SDK会自动将Collection(Edm.ComplexType)类型的字段映射为List<SearchDocument>返回,没有额外解析成本。 - 如果需要做序列化传输,直接用Jackson序列化
SearchDocument实例本身即可得到标准JSON,不要依赖toString()的输出做数据传递。
方案2:针对现有toString格式字符串的解析实现
如果只能拿到这种格式的字符串,可以通过逐字符状态扫描的方式解析,精准识别字段分隔符和值内部的逗号,Java 8实现代码如下:
import com.azure.search.documents.models.SearchDocument; import java.util.ArrayList; import java.util.List; public class SearchDocParser { public static List<SearchDocument> parseToDocList(String raw) { List<SearchDocument> docList = new ArrayList<>(); String content = raw.trim(); // 移除首尾包裹的中括号 content = content.substring(1, content.length() - 1).trim(); if (content.isEmpty()) return docList; int idx = 0; int length = content.length(); while (idx < length) { // 定位单个文档对象的起始位置 if (content.charAt(idx) == '{') { idx++; SearchDocument currentDoc = new SearchDocument(); StringBuilder keyBuilder = new StringBuilder(); StringBuilder valBuilder = new StringBuilder(); boolean readingKey = true; // 扫描到当前对象的结束}为止 while (idx < length && content.charAt(idx) != '}') { char currentChar = content.charAt(idx); if (readingKey) { if (currentChar == '=') { readingKey = false; idx++; continue; } if (currentChar != ' ') keyBuilder.append(currentChar); } else { // 判断当前逗号是否为字段分隔符:逗号后为空格,且空格后能匹配到下一个key=结构 if (currentChar == ',' && idx + 1 < length && content.charAt(idx+1) == ' ') { int checkPos = idx + 2; while (checkPos < length && content.charAt(checkPos) == ' ') checkPos++; int tempPos = checkPos; while (tempPos < length && content.charAt(tempPos) != '=' && content.charAt(tempPos) != ',' && content.charAt(tempPos) != '}') { tempPos++; } if (tempPos < length && content.charAt(tempPos) == '=') { // 确认为字段分隔符,存入当前键值对 currentDoc.put(keyBuilder.toString().trim(), valBuilder.toString().trim()); keyBuilder.setLength(0); valBuilder.setLength(0); readingKey = true; idx += 2; continue; } } valBuilder.append(currentChar); } idx++; } // 存入当前对象最后一个键值对 if (keyBuilder.length() > 0) { currentDoc.put(keyBuilder.toString().trim(), valBuilder.toString().trim()); } docList.add(currentDoc); } idx++; } return docList; } public static void main(String[] args) { String testStr = "[{empId=abc, empName=name, empSal=100, empEmail=test@gmail.com}, {empId=xyz, empName=test,abcexample, empSal=200, empEmail=test12@gmail.com}]"; List<SearchDocument> result = parseToDocList(testStr); // 验证输出:第二个文档的empName字段值为test,abcexample,不会被逗号拆分 result.forEach(doc -> System.out.printf("empName: %s, empSal: %s%n", doc.get("empName"), doc.get("empSal"))); } }
原有方案失效原因
- 直接调用Jackson
readValue:输入不符合JSON语法规范,默认反序列化器无法识别=分隔的键值对、无双引号的键名。 - 逗号分割+StringTokenizer方案:只做了固定字符分割,没有上下文判断逻辑,会把字段值内部的逗号误判为字段分隔符,导致键值匹配错位。
注意:如果你的复杂类型字段存在嵌套对象、数组结构,上述解析逻辑需要补充括号层级匹配的判断,这种场景下优先使用SDK原生取值的方案,稳定性最高。
内容的提问来源于stack exchange,提问作者Raj Kishore
相关产品推荐
相关产品推荐

