6

我正在使用 Jackson (1.9.x) 库将 JSON 解析为 Map:

ObjectMapper mapper = new ObjectMapper();
Map<String,Object> map = (Map<String,Object>) mapper.readValue(jsonStr, Map.class);

有没有办法告诉杰克逊解析器小写所有的键名?我尝试使用 Jackson PropertyNamingStrategy,但这不起作用——它似乎只有在映射到某个 bean 时才有用,而不是 Map。

说明:

  1. 我不想为 JSON 预先创建 bean - 我只想要动态地图
  2. 传入的 JSON 键不会是小写的,但我希望所有映射键都是小写的(参见下面的示例)
  3. JSON 相当大且嵌套严重,因此根本不需要在 Jackson 解析后手动替换传入 JSON 的正则表达式或创建新映射。

传入的 JSON:

{"CustName":"Jimmy Smith","Result":"foo","CustNo":"1234"}

Java 地图将具有:

"custname" => "Jimmy Smith"
"result" => "foo"
"custno" => "1234"

[更新]:我在下面给出的答案并不能完全解决问题。仍在寻找解决方案。

4

5 回答 5

5

(nb 此解决方案仅使用 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"
                            )
                    )));
}
于 2015-10-10T13:25:11.093 回答
4

我想出了一种方法。使用 a org.codehaus.jackson.map.KeyDeserializer,将其放入 aSimpleModule并将该模块注册到 Jackson 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"}}
于 2013-09-17T01:23:53.023 回答
2

对于杰克逊,没有任何功能可以以嵌套方式降低键。至少我知道的不是。我编写了这个简单的递归代码来完成这项工作。

    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 对象的嵌套程度。

于 2015-03-26T21:24:49.980 回答
0
public void setKeyName(String systemName){
    this.systemName = systemName.toLowerCase();
}
于 2018-01-12T07:41:02.380 回答
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

于 2019-09-03T03:39:44.083 回答