我正在使用Jackson(1.9.x)库将JSON解析为Map:
ObjectMapper mapper = new ObjectMapper();
Map<String,Object> map = (Map<String,Object>) mapper.readValue(jsonStr, Map.class);
有没有办法告诉Jackson解析器小写所有键的名称?我尝试使用Jackson PropertyNamingStrategy,但是这不起作用 - 当它被映射到某个bean而不是Map时它似乎很有用。
澄清:
传入JSON:
{"CustName":"Jimmy Smith","Result":"foo","CustNo":"1234"}
Java地图将具有:
"custname" => "Jimmy Smith"
"result" => "foo"
"custno" => "1234"
[更新] :我在下面给出的答案并未完全解决问题。仍在寻找解决方案。
答案 0 :(得分:4)
我想出了一种方法。使用org.codehaus.jackson.map.KeyDeserializer
,将其放入SimpleModule
并使用杰克逊ObjectMapper
注册该模块。
import org.codehaus.jackson.map.KeyDeserializer;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.Version;
// ...
class LowerCaseKeyDeserializer extends KeyDeserializer {
@Override
public Object deserializeKey(String key, DeserializationContext ctx)
throws IOException, JsonProcessingException {
return key.toLowerCase();
}
}
// ...
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("LowerCaseKeyDeserializer",
new Version(1,0,0,null));
module.addKeyDeserializer(Object.class, new LowerCaseKeyDeserializer());
mapper.registerModule(module);
Map<String,Object> map =
(Map<String,Object>) mapper.readValue(jsonStr, Map.class);
[更新] :实际上这只是小写顶级地图键,而不是嵌套键。
如果输入为:
{"CustName":"Jimmy Smith","CustNo":"1234","Details":{"PhoneNumber": "555-5555", "Result": "foo"}}
不幸的是,地图中的输出将是:
{"custname"="Jimmy Smith", "custno"="1234", "details"={"PhoneNumber"="555-5555", "Result"="foo"}}
答案 1 :(得分:2)
(这个解决方案仅用Jackson 2测试)
可以通过包装JsonParser并将.toLowerCase()
应用于所有字段名称来实现此目的:
private static final class DowncasingParser extends JsonParserDelegate {
private DowncasingParser(JsonParser d) {
super(d);
}
@Override
public String getCurrentName() throws IOException, JsonParseException {
if (hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
return delegate.getCurrentName().toLowerCase();
}
return delegate.getCurrentName();
}
@Override
public String getText() throws IOException, JsonParseException {
if (hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
return delegate.getText().toLowerCase();
}
return delegate.getText();
}
}
然后你必须有一个自定义的JsonFactory来应用你的包装器,就像在这个测试中一样:
@Test
public void downcase_map_keys_by_extending_stream_parser() throws Exception {
@SuppressWarnings("serial")
ObjectMapper mapper = new ObjectMapper(new JsonFactory() {
@Override
protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException {
return new DowncasingParser(super._createParser(data, offset, len, ctxt));
}
@Override
protected JsonParser _createParser(InputStream in, IOContext ctxt) throws IOException {
return new DowncasingParser(super._createParser(in, ctxt));
}
@Override
protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException {
return new DowncasingParser(super._createParser(r, ctxt));
}
@Override
protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, boolean recyclable)
throws IOException {
return new DowncasingParser(super._createParser(data, offset, len, ctxt, recyclable));
}
});
assertThat(
mapper.reader(Map.class)
.with(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)
.with(JsonParser.Feature.ALLOW_SINGLE_QUOTES)
.readValue("{CustName:'Jimmy Smith', CustNo:'1234', Details:{PhoneNumber:'555-5555',Result:'foo'} } }"),
equalTo((Map<String, ?>) ImmutableMap.of(
"custname", "Jimmy Smith",
"custno", "1234",
"details", ImmutableMap.of(
"phonenumber", "555-5555",
"result", "foo"
)
)));
}
答案 2 :(得分:1)
对于杰克逊来说,没有任何功能会以嵌套的方式降低键。至少不是我所知道的。我写了这个简单的递归代码来完成这项工作。
public JSONObject recursiveJsonKeyConverterToLower(JSONObject jsonObject) throws JSONException
{
JSONObject resultJsonObject = new JSONObject();
@SuppressWarnings("unchecked") Iterator<String> keys = jsonObject.keys();
while(keys.hasNext())
{
String key = keys.next();
Object value = null;
try
{
JSONObject nestedJsonObject = jsonObject.getJSONObject(key);
value = this.recursiveJsonKeyConverterToLower(nestedJsonObject);
}
catch(JSONException jsonException)
{
value = jsonObject.get(key);
}
resultJsonObject.put(key.toLowerCase(), value);
}
return resultJsonObject;
}
传递字符串:
String json = "{'Music': 0, 'Books': {'Biology': 1.1, 'Chemistry': {'Inorganic': true, 'Organic': ['Atom', 'Molecule']}}, 'Food': {'Chicken': [1, 2, 3]}}";
输出:
{"music":0,"books":{"biology":1.1,"chemistry":{"inorganic":true,"organic":["Atom","Molecule"]}},"food":{"chicken":[1,2,3]}}
通过使Map<String, Object>
成为JSONObject
类型以及其他一些小调整,也很容易获得resultJsonObject
而不是Map
(这就是您想要的)。
警告:对于嵌套的JSON,结果类型为Map<String, Map<String, Object>>
,具体取决于json对象的嵌套方式。
答案 3 :(得分:0)
public void setKeyName(String systemName){
this.systemName = systemName.toLowerCase();
}
答案 4 :(得分:0)
下面是第二条JSON消息:
{
"ModeL":"Tesla",
"YeaR":"2015"
}
通常,默认ObjectMapper无法将此消息反序列化为CarInfo对象。通过以下配置,可以实现:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
CarInfo info = objectMapper.readValue(data, CarInfo.class); //'data' contains JSON string
此反序列化有效。他的反序列化是有效的。
https://mtyurt.net/post/jackson-case-insensitive-deserialization.html