使用 Jackson 数据绑定跳过嵌套字段?

Skipping nested field with Jackson data binding?

我必须如何将以下 json 片段绑定(双向)到 Jackson 下方的 Java 对象?

我希望 key/value 对 Json 时事通讯节点最终出现在时事通讯字段的地图中。 绑定这个的 Jackson 配置(注释和其他)必须是什么? 抱歉,我不知道如何:((使用最新版本的 Jackson)。

下面的Json格式是固定的,我从第三方rest服务那里得到的(我不能改变它)。我可以更改 java 代码,但更喜欢下面的设置。

JSON:

{
    "newsletter": {
        "key1": "value1",
        "key2": "value2",
        "key3": "value3"
    }
}

Java对象:

class Preferences {
    private NewsLetter newsLetter;
    // getter/setter for field newsLetter.
}

class NewsLettter {
    private Map<String, String> properties;
    // getter/setter for field properties.
}

以下是 JSON 到 Java 转换的工作原理

第一步是创建 Java class 来保存 JSON 数据。你已经有一个了。看看json。您创建一个 Preferences 对象来保存 整个 json。 json 包含一个 newsletter 元素。你 在 Preferences 对象中创建类型为 Newsletter 的 Java 对象。 创建 org.codehaus.jackson.map.ObjectMapper class 的实例。这是 class 将 JSON 映射到 Java 对象。

 ObjectMapper mapper = new ObjectMapper();

然后使用ObjectMapperreadValue方法进行读取。此方法有多个版本,您可以使用接受一个 String。但是,有些方法可以从 URLFileInputStreamStringByteArray.

读取

ObjectMapper 缓存序列化器和反序列化器,因此将一个 ObjectMapper 实例重用于多个实例是个好主意 转换 如果您有一个 String 然后将它传递给 Jackson 以将其转换为 Java Object.

String str = "
    { \"newsletter\":
      {
        \"properties\": {
         \"key1\" : \"value1\",
         \"key2\" : \"value2\",
         \"key3\" : \"value3\"
      }
     }
   }";

//read json
Preferences preferences = mapper.readValue(str, Preferences.class);
//write json
mapper.writeValue(str, preferences);

您可以使用将 json 转义为 java 字符串的工具:http://www.freeformatter.com/java-dotnet-escape.html

此工具产生以下结果

{ \"newsletter\":\r\n          {\r\n            \"properties\": {\r\n             \"key1\" : \"value1\",\r\n             \"key2\" : \"value2\",\r\n             \"key3\" : \"value3\"\r\n          }\r\n         }\r\n        }

如果你能得到一个道具JSON,像这样:

{
    "newsletter": {
        "key1": "value1",
        "key2": "value2",
        "key3": "value3"
    }
}

(注意 , 作为分隔符而不是 ;)

然后您可以使用以下 class 来保存您的 JSON 数据:

class Preferences {
    @JsonProperty("newsletter")
    private Map<String, String> newsLetter;

    @Override
    public String toString() {
        return "Preferences{" + "newsLetter=" + newsLetter + '}';
    }
}

在这里,您不再使用 NewsLetter class,因为您需要一个嵌套键,就像 Roman C 在他的回答中所说的那样。只需将 Map 字段直接添加到您的 Preferences class 即可。

您可以这样阅读您的 JSON:

public static void main(String[] args) throws Exception {
    ObjectMapper om = new ObjectMapper();
    System.out.println(om.readValue(new File("example.json"), Preferences.class));
}

它将打印:

Preferences{newsLetter={key1=value1, key2=value2, key3=value3}}

我想我成功了。 我用

  • @JsonValue 创建时事通讯的 JSON 部分。
  • @JsonCreator 使用地图创建 NewsLetter 实例 包含 key/value 对。
  • @JsonTypeInfo 指示使用哪个实现来创建 NewsLetter 实例。

使用上面的 json 代码段,测试 运行 双向都很好。 在代码中(首选项 class 与原始问题中列出的相同):

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.EXISTING_PROPERTY, defaultImpl = NewsLetterPreferencesDefault.class)
public interface NewsLetterPreferences  {
}

public final class NewsLetterPreferencesDefault {
    private Map<String, String> properties;

    @JsonCreator
    public NewsLetterPreferencesDefault(final Map<String, String> properties) {
        this.properties = properties.
    }

    @JsonValue
    public Map<String, String> getAllProperties() {
        return this.properties;
    }
}

有办法吗?我以前从未使用过@JsonTypeInfo,所以我希望它是正确的。我会 google 一些更多的例子。感谢所有反馈。

您可以简单地使用 @JsonAnySetter 进行反序列化

Marker annotation that can be used to define a non-static, two-argument method (first argument name of property, second value to set), to be used as a "fallback" handler for all otherwise unrecognized properties found from JSON content.

@JsonAnyGetter用于序列化。

Marker annotation that can be used to define a non-static, no-argument method or member field as something of a reverse of JsonAnySetter method; basically being used like a getter but such that contents of the returned Map (type must be Map) are serialized as if they were actual properties of the bean that contains method/field with this annotations

public class Newsletter {

    private final Map<String, String> properties = new HashMap<>();

    @JsonAnySetter
    public void addProperty(String name, String value) {
        properties.put(name, value);
    }

    @JsonAnyGetter
    public Map<String, String> getProperties() {
        return properties;
    }
}

这对于您的用例来说应该可以正常工作。

测试

public class Main {

    public static void main(String[] args) throws Exception {
        String json
                = "{\n"
                + "    \"newsletter\": {\n"
                + "        \"key1\": \"value1\",\n"
                + "        \"key2\": \"value2\",\n"
                + "        \"key3\": \"value3\"\n"
                + "    }\n"
                + "}";

        ObjectMapper mapper = new ObjectMapper();
        Preferences prefs = mapper.readValue(json, Preferences.class);
        Map<String, String> properties = prefs.getNewsletter().getProperties();
        for (Map.Entry<String, String> prop: properties.entrySet()) {
            System.out.println(prop.getKey() + ":" + prop.getValue());
        }
    }
}

另请参阅: