如何将特定格式字符串解析为Java的Map<String,Map<String,Object>>?
解决方案:解析特殊格式字符串为Java嵌套Map
你的输入格式不是标准JSON,这就是Jackson直接解析失败的核心原因——它的数字键、key/value这些字段名,还有数组里的元素都没有加双引号,完全不符合JSON语法规范。下面给你两种实用的解决思路:
方法一:预处理字符串为标准JSON(推荐,实现简单)
我们可以通过正则表达式批量替换,把原始字符串转换成标准JSON,再用Jackson正常解析。具体步骤如下:
- 给所有无引号的键(包括外层数字键和内层的
key/value)加上双引号 - 给数组中无引号的元素加上双引号
- 把格式里的等号替换成JSON标准的冒号
代码示例
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; public class ParserExample { public static void main(String[] args) throws Exception { String singledoc = "{ 6138249={ value=[multi2, multi3, multi4], key=TestMulticat }, 6161782={ value=Traps (Bamboo / Box), key=Observation gear } }"; // 1. 给外层数字键加引号 String jsonStr = singledoc.replaceAll("\\b(\\d+)\\b", "\"$1\""); // 2. 给key/value字段名加引号 jsonStr = jsonStr.replaceAll("\\b(key|value)\\b", "\"$1\""); // 3. 给数组里的无引号元素加引号 jsonStr = jsonStr.replaceAll("\\[(.*?)\\]", m -> { String inner = m.group(1).replaceAll("\\b([a-zA-Z0-9]+)\\b", "\"$1\""); return "[" + inner + "]"; }); // 4. 把等号替换成JSON的冒号 jsonStr = jsonStr.replaceAll("=", ":"); // 用Jackson解析成目标类型 ObjectMapper objectMapper = new ObjectMapper(); Map<String, Map<String, Object>> data = objectMapper.readValue( jsonStr, new TypeReference<Map<String, Map<String, Object>>>() {} ); // 验证解析结果 System.out.println(data.get("6138249").get("value")); // 输出 [multi2, multi3, multi4] System.out.println(data.get("6161782").get("key")); // 输出 Observation gear } }
方法二:自定义Jackson反序列化器(无需修改原始字符串)
如果不想修改原始字符串,可以给Jackson写一个自定义反序列化器,手动处理这种特殊格式。这种方式更灵活,但实现稍复杂:
代码示例
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class CustomMapDeserializer extends JsonDeserializer<Map<String, Map<String, Object>>> { @Override public Map<String, Map<String, Object>> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { Map<String, Map<String, Object>> outerMap = new HashMap<>(); p.nextToken(); // 跳过最外层的{ while (p.nextToken() != JsonToken.END_OBJECT) { // 读取外层数字键 String outerKey = p.getText(); p.nextToken(); // 跳过= p.nextToken(); // 进入内层的{ Map<String, Object> innerMap = new HashMap<>(); while (p.nextToken() != JsonToken.END_OBJECT) { String innerKey = p.getText(); p.nextToken(); // 跳过= Object value; if (p.getCurrentToken() == JsonToken.START_ARRAY) { // 处理数组类型的值 List<String> arrayValue = new ArrayList<>(); while (p.nextToken() != JsonToken.END_ARRAY) { arrayValue.add(p.getText()); } value = arrayValue; } else { // 处理普通字符串值 value = p.getText(); } innerMap.put(innerKey, value); } outerMap.put(outerKey, innerMap); } return outerMap; } public static void main(String[] args) throws Exception { String singledoc = "{ 6138249={ value=[multi2, multi3, multi4], key=TestMulticat }, 6161782={ value=Traps (Bamboo / Box), key=Observation gear } }"; ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(Map.class, new CustomMapDeserializer()); objectMapper.registerModule(module); Map<String, Map<String, Object>> data = objectMapper.readValue( singledoc, new TypeReference<Map<String, Map<String, Object>>>() {} ); // 验证结果 System.out.println(data); } }
注意事项
- 方法一的正则表达式是针对你的特定格式编写的,如果后续输入值中包含特殊字符(比如带空格的数组元素),需要调整正则规则适配
- 方法二的自定义反序列化器可以根据实际输入的格式细节进一步优化,比如处理更复杂的嵌套结构
内容的提问来源于stack exchange,提问作者user2903536
相关产品推荐
相关产品推荐

