如何使用Jackson JSON将JSON字符串转换为Map<String, String>?

251

我想尝试做类似这样的事情,但它不起作用:

Map<String, String> propertyMap = new HashMap<String, String>();

propertyMap = JacksonUtils.fromJSON(properties, Map.class);

但是IDE说:

未检查的赋值:Map to Map<String,String>

这该怎么做呢?我只是使用Jackson,因为它已经在项目中可用,有没有本地的Java方式可以进行JSON转换?

在PHP中,我只需使用json_decode($str)即可获得一个数组。我需要基本相同的东西。


JacksonUtils这个类是从哪里来的?我在任何一个Jackson版本中都没有看到它。 - Rob Heiser
这是我们的Jackson包装器,处理一些必须完成的JsonFactory和ObjectMapper工作。 - adamJLev
1
所以问题在于JacksonUtils.fromJSON()没有声明返回Map<String, String>,而只是Map。 - Rob Heiser
8
顺便说一句,在第一行不要分配新的HashMap:那会被忽略。只需分配调用即可。 - StaxMan
标题与您所描述的问题无关,这与未经过类型定义的集合有关。下面的答案是对您实际上想要询问的问题的正确回答。 - Jukka Dahlbom
11个回答

419

[2020年9月更新] 尽管我多年前在这里给出的原始答案似乎很有帮助,并且仍然得到了赞同,但我现在使用Google的GSON库,我发现它更直观。

我有以下代码:

public void testJackson() throws IOException {  
    ObjectMapper mapper = new ObjectMapper(); 
    File from = new File("albumnList.txt"); 
    TypeReference<HashMap<String,Object>> typeRef 
            = new TypeReference<HashMap<String,Object>>() {};

    HashMap<String,Object> o = mapper.readValue(from, typeRef); 
    System.out.println("Got " + o); 
}   

它正在从文件中读取,但是mapper.readValue()也可以接受一个InputStream,你可以通过以下方式从字符串中获取一个InputStream
new ByteArrayInputStream(astring.getBytes("UTF-8")); 

关于映射器的更多解释可以在我的博客上找到。

2
@Suraj,这是根据文档的要求,我同意我无法从第一原则上推导出这个公式。这并不奇怪,只是表明Java比我们想象的更加复杂。 - djna
1
Krige:我原以为最难的是让映射器运行起来,但我已经添加了如何将该技术应用于字符串的说明。 - djna
2
一个小注释:创建JsonFactory的第一行是不必要的。ObjectMapper可以自动创建它。 - StaxMan
2
@djna,发帖者要求的是Map<String, String>,而你提供的是Map<String, Object> - fIwJlxSzApHEZIl
要将Map写成字符串,您可以执行mapper.writeValueAsString(hashmap) - Zaheer

69

尝试使用TypeFactory。这是Jackson JSON (2.8.4)的代码。

Map<String, String> result;
ObjectMapper mapper;
TypeFactory factory;
MapType type;

factory = TypeFactory.defaultInstance();
type    = factory.constructMapType(HashMap.class, String.class, String.class);
mapper  = new ObjectMapper();
result  = mapper.readValue(data, type);

这是较旧版本的Jackson JSON的代码。

Map<String, String> result = new ObjectMapper().readValue(
    data, TypeFactory.mapType(HashMap.class, String.class, String.class));

44
TypeFactory.mapType(...)已被弃用,请尝试使用以下代码:new TypeReference<HashMap<String,String>>() {} - cyber-monk
3
@cyber-monk 这样可以消除警告,但实际上并没有检查类型。 - David Moles

38

注意到的警告是由编译器而不是库(或实用方法)产生的。

直接使用Jackson的最简单方法是:

HashMap<String,Object> props;

// src is a File, InputStream, String or such
props = new ObjectMapper().readValue(src, new TypeReference<HashMap<String,Object>>() {});
// or:
props = (HashMap<String,Object>) new ObjectMapper().readValue(src, HashMap.class);
// or even just:
@SuppressWarnings("unchecked") // suppresses typed/untype mismatch warnings, which is harmless
props = new ObjectMapper().readValue(src, HashMap.class);

你调用的实用方法可能只是执行了类似于这样的操作。


2
OP 要求的是 Map<String, String>,而你提供的是 Map<String, Object> - fIwJlxSzApHEZIl

25
ObjectReader reader = new ObjectMapper().readerFor(Map.class);

Map<String, String> map = reader.readValue("{\"foo\":\"val\"}");

请注意reader实例是线程安全的。


1
@dpetruha OP 要求的是 Map<String, String>,而你提供的是 Map<String, Object> - fIwJlxSzApHEZIl

10

将字符串转换为JSON Map:

Map<String,String> map = new HashMap<String,String>();

ObjectMapper mapper = new ObjectMapper();

map = mapper.readValue(string, HashMap.class);

5
以上仍会导致“类型安全: 表达式类型为HashMap需要未经检查的转换以符合Map<String,String>”,虽然可以使用@SuppressWarnings注释来抑制此警告,但我建议首先使用TypeReference或像Staxman所提到的那样进行转换。 - Karthic Raghupathi
1
为了消除类型安全警告,您可以使用map = mapper.readValue(string, map.getClass()); - 假设您已经实例化了该映射,就像这里的情况一样。 - MJV
如果 var 的类型是 Map<Integer, String>,仅获取对象类不正确。 - Jiayu Wang

9

我想给出一个关于Kotlin的答案。

val propertyMap = objectMapper.readValue<Map<String,String>>(properties, object : TypeReference<Map<String, String>>() {})

1
不太确定为什么这个被踩了。这行代码不仅对我有效,而且还提供了正确实现的关键。通过将Map<String,String>替换为所需的其他类型,映射器将把字符串转换为任何复杂集合,例如List<MyObject>或List<Map<String,MyObject>>。 - Dustin
因为问题是“如何在Java中实现”。 - NaN
1
在Java问题中得到Kotlin特定的答案总是很有用的,因为Kotlin通常被用作JVM上Java的替代品。我很感激这个Kotlin的答案。 - Christian Hujer
Kotlin截至2023年val fromJson: Map<String, String> = objectMapper.readValue(json) - Mike Holdsworth

6
JavaType javaType = objectMapper.getTypeFactory().constructParameterizedType(Map.class, Key.class, Value.class);
Map<Key, Value> map=objectMapper.readValue(jsonStr, javaType);

我认为这将解决您的问题。

1
在Java文档中:@since 2.5 -- 但可能会在2.7或2.8中被弃用(在2.7中不需要)。 - Al-Mothafar

5
以下方法适用于我:
Map<String, String> propertyMap = getJsonAsMap(json);

这里的getJsonAsMap定义如下:

public HashMap<String, String> getJsonAsMap(String json)
{
    try
    {
        ObjectMapper mapper = new ObjectMapper();
        TypeReference<Map<String,String>> typeRef = new TypeReference<Map<String,String>>() {};
        HashMap<String, String> result = mapper.readValue(json, typeRef);

        return result;
    }
    catch (Exception e)
    {
        throw new RuntimeException("Couldnt parse json:" + json, e);
    }
}

请注意,如果您的json中有子对象(因为它们不是String,而是另一个HashMap),则此方法将失败,但如果您的json是属性键值列表,则此方法将起作用。
{
    "client_id": "my super id",
    "exp": 1481918304,
    "iat": "1450382274",
    "url": "http://www.example.com"
}

2

使用Google的Gson

为什么不像这里提到的那样使用Google的Gson呢?

非常直接并且对我很有帮助:

HashMap<String,String> map = new Gson().fromJson( yourJsonString, new TypeToken<HashMap<String, String>>(){}.getType());

1
同意,世界已经发生了变化,Gson使用起来要简单得多。 - djna

1
这是解决这个问题的通用方案。
public static <K extends Object, V extends Object> Map<K, V> getJsonAsMap(String json, K key, V value) {
    try {
      ObjectMapper mapper = new ObjectMapper();
      TypeReference<Map<K, V>> typeRef = new TypeReference<Map<K, V>>() {
      };
      return mapper.readValue(json, typeRef);
    } catch (Exception e) {
      throw new RuntimeException("Couldnt parse json:" + json, e);
    }
  }

希望有一天有人能够想到创建一个实用方法来将任何键/值类型转换为Map,因此有了这个答案 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接