如何将值为json的Map <String,String>转换为POJO

时间:2020-02-06 07:49:53

标签: java jackson

例如,我们的目标类型为Info,定义如下

class Info {
  Person person;
  // a dozen of other properties
  // getters and setters
}

class Person {
  String name;
  int age;

  // getters and setters
}

我们的地图就像Collections.singletonMap("person", "{\"name\": \"nick\", \"age\": 18}")

如何将此Map转换为Info对象? (为Person添加一个构造函数或一个一个地解析值是不可行的,因为实际上有很多属性(例如Person)。)

尝试将杰克逊与objectMapper.convertValue一起使用,但它抛出异常,消息为no String-argument constructor/factory method to deserialize from String value

4 个答案:

答案 0 :(得分:1)

Jackson的ObjectMapper知道如何将Map转换为POJO,但是它无法将带有JSON文本的字符串映射到POJO,因此您首先必须将JSON文本解析为某种东西通用(MapJsonNode树)。

基本上,将您的Map<String, String>转换为Map<String, JsonNode>

Map<String, String> data = Collections.singletonMap("person", "{\"name\": \"nick\", \"age\": 18}");

ObjectMapper mapper = new ObjectMapper();
Map<String, Object> dataTree = new HashMap<>();
for (Entry<String, String> entry : data.entrySet())
    dataTree.put(entry.getKey(), mapper.readTree(entry.getValue()));
Info info = mapper.convertValue(dataTree, Info.class);

System.out.println("name = " + info.getPerson().getName());
System.out.println("age = " + info.getPerson().getAge());

输出

name = nick
age = 18

答案 1 :(得分:1)

如果您确实有一个Map<String, String>,键是您的字段名称,值是一个json结构,那么没有其他机会了:

switch (map.getKey()) { 
    case "person": 
           Person person = mapper.readValue(map.getValue(), Person.class);
           info.setPerson(person);
           break;
    //TODO add your other Map-Keys here
}

但是,如果您具有正常的json结构,例如:

{
   "person": {
      "name": "nick",
      "age": 18
   }
}

那么您可以简单地:

Info info = mapper.readValue(json, Info.class);

答案 2 :(得分:0)

您可以使用Google创建的用于处理json结构的Gson库轻松实现此目的。

Map<String, String> data = Collections.singletonMap("person", "{\"name\": \"nick\", \"age\": 18}");
Person person = new Gson.fromJson(data.get("person"), Person.class);
Info info = new Info();
info.setPerson(person);

答案 3 :(得分:0)

好的,看完源代码之后,我想出了一个解决方案:

首先,自定义BeanDeserialzier

class StringBeanDeserializer extends BeanDeserializer {
    private ObjectMapper objectMapper;
    public StringBeanDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc, BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs, HashSet<String> ignorableProps, boolean ignoreAllUnknown, boolean hasViews, ObjectMapper objectMapper) {
        super(builder, beanDesc, properties, backRefs, ignorableProps, ignoreAllUnknown, hasViews);
        this.objectMapper = objectMapper == null ? new ObjectMapper() : objectMapper;
    }

    @Override
    public Object deserializeFromString(JsonParser p, DeserializationContext ctxt) throws IOException {
        if (this._beanType.isTypeOrSubTypeOf(CharSequence.class)) {
            return super.deserializeFromString(p, ctxt);
        }
        return objectMapper.readValue(p.getText(), this._beanType.getRawClass());
    }
}

然后编写一个Builder来生产BeanDeserialzier

class StringBeanDeserializerBuilder extends BeanDeserializerBuilder {
    private ObjectMapper objectMapper;
    public StringBeanDeserializerBuilder(BeanDescription beanDesc, DeserializationContext ctxt) {
        super(beanDesc, ctxt);
    }

    public StringBeanDeserializerBuilder(BeanDescription beanDesc, DeserializationContext ctxt, ObjectMapper objectMapper) {
        this(beanDesc, ctxt);
        this.objectMapper = objectMapper;
    }

    protected StringBeanDeserializerBuilder(BeanDeserializerBuilder src) {
        super(src);
    }

    /**
     * Method for constructing a {@link BeanDeserializer}, given all
     * information collected.
     */
    public JsonDeserializer<?> build()
    {
        Collection<SettableBeanProperty> props = _properties.values();
        _fixAccess(props);
        BeanPropertyMap propertyMap = BeanPropertyMap.construct(props,
                _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES),
                _collectAliases(props));
        propertyMap.assignIndexes();

        // view processing must be enabled if:
        // (a) fields are not included by default (when deserializing with view), OR
        // (b) one of properties has view(s) to included in defined
        boolean anyViews = !_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
        if (!anyViews) {
            for (SettableBeanProperty prop : props) {
                if (prop.hasViews()) {
                    anyViews = true;
                    break;
                }
            }
        }

        // one more thing: may need to create virtual ObjectId property:
        if (_objectIdReader != null) {
            /* 18-Nov-2012, tatu: May or may not have annotations for id property;
             *   but no easy access. But hard to see id property being optional,
             *   so let's consider required at this point.
             */
            ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader, PropertyMetadata.STD_REQUIRED);
            propertyMap = propertyMap.withProperty(prop);
        }

        return new StringBeanDeserializer(this,
                _beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown,
                anyViews, objectMapper);
    }
}

最后写出该Builder的Factory使用

public class StringBeanDeserializeFactory extends BeanDeserializerFactory {
    private ObjectMapper objectMapper = new ObjectMapper();

    /**
     * Globally shareable thread-safe instance which has no additional custom deserializers
     * registered
     */
    public final static BeanDeserializerFactory instance = new StringBeanDeserializeFactory(
            new DeserializerFactoryConfig());

    public StringBeanDeserializeFactory(DeserializerFactoryConfig config) {
        super(config);
    }

    /**
     * Overridable method that constructs a {@link BeanDeserializerBuilder}
     * which is used to accumulate information needed to create deserializer
     * instance.
     */
    protected BeanDeserializerBuilder constructBeanDeserializerBuilder(DeserializationContext ctxt,
                                                                       BeanDescription beanDesc) {
        return new StringBeanDeserializerBuilder(beanDesc, ctxt, objectMapper);
    }
}

用法示例:

        ObjectMapper objectMapper = new ObjectMapper(null, null, new DefaultDeserializationContext.Impl(StringBeanDeserializeFactory.instance));
        Map<String, String> map = ImmutableMap.of("person", objectMapper.writeValueAsString(new Person("nick", 25)),
                "raw", "test");
        System.out.println(objectMapper.convertValue(map, Data.class));
相关问题