使用杰克逊数据绑定跳过嵌套字段?
Posted
技术标签:
【中文标题】使用杰克逊数据绑定跳过嵌套字段?【英文标题】:Skipping nested field with Jackson data binding? 【发布时间】:2015-10-23 11:47:22 【问题描述】:我必须如何使用 Jackson 将以下 json sn-p 绑定(双向)到下面的 Java 对象?
我希望 Json newsletter 节点的键/值对最终出现在 newsletter 字段的 Map 中。 绑定 this 的 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格式是固定的,我是从第三方rest服务收到的”第三方服务生成无效的JSON?顺便说一句,您需要保留NewsLettter
课程吗?
我喜欢 NewsLetter 类,但如果你有一个好主意,可以删除/修改它。打我;)
【参考方案1】:
您可以简单地使用@JsonAnySetter
进行反序列化
可用于定义非静态、双参数方法(属性的第一个参数名称,要设置的第二个值)的标记注释,用作从 JSON 中找到的所有其他无法识别的属性的“后备”处理程序内容。
还有@JsonAnyGetter
用于序列化。
可用于将非静态、无参数方法或成员字段定义为与
JsonAnySetter
方法相反的标记注释;基本上像 getter 一样使用,但是返回的 Map 的内容(类型必须是Map
)被序列化,就好像它们是包含带有此注释的方法/字段的 bean 的实际属性
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());
另请参阅:
Jackson tips: using @JsonAnyGetter/@JsonAnySetter to create "dyna beans"【讨论】:
谢谢。我只是完全按照您上面的建议使用它,并且反序列化工作正常:实例包含带有以下内容的映射:“key1=value1, key2=value2, key3=value3” 但是,将其序列化回 JSON 会导致:“” newsLetters":"emptyProperties":false,"key1":"value1","key2":"value2","key3":"value3"" 我不知道这个额外的键/值对“emptyProperties”在哪里/false”来自(不是我的)。任何想法 ?也许来自@JsonTypeInfo?...我使用 ObjectMapper.writeValueAsString(obj)... 是的,我不知道。我刚刚使用上面的示例进行了测试,使用S.o.p(mapper.writeValueAsString(prefs))
并且我没有看到额外的属性。 Newsletter
是具有该属性的其他类的子类吗?
感谢您的快速回复。您如何向映射器指定要使用的 NewsLetter 实现? NewsLetterDefault 实现从 MapProperties@JsonIgnore
添加到问题属性?还是我误解了你在说什么?没有财产吗?这就是你所说的“幽灵”吗?
如果@JsonValue
有效,我认为创建mixin 没有任何好处。我会同意的【参考方案2】:
如果你能得到一个合适的 JSON,像这样:
"newsletter": "key1": "value1", "key2": "value2", "key3": "value3"
(注意,
是分隔符而不是;
)
那么您可以使用以下类来保存您的 JSON 数据:
class Preferences
@JsonProperty("newsletter")
private Map<String, String> newsLetter;
@Override
public String toString()
return "Preferences" + "newsLetter=" + newsLetter + '';
在这里,您不再使用 NewsLetter
类,因为您需要一个嵌套键,就像 Roman C 在他的回答中所说的那样。只需将Map
字段直接添加到您的Preferences
类中即可。
您可以像这样读取您的 JSON:
public static void main(String[] args) throws Exception
ObjectMapper om = new ObjectMapper();
System.out.println(om.readValue(new File("example.json"), Preferences.class));
它会打印:
偏好newsLetter=key1=value1, key2=value2, key3=value3
【讨论】:
感谢您的解决方案。抱歉,分隔符错误,刚刚更正。几天前我试过这个,效果很好。但我喜欢在他自己的类中抽象 Newsletter 的东西,这样我就可以添加其他东西,比如查找器和其他操作方法。使用您的解决方案,NewsLetter 类的类 Preferences 和内部变得依赖(紧密耦合)。 如果您无法更改 JSON,那么我所知道的唯一方法仍然是使用 Jackson ist 删除地图并为NewsLetter
类中的每个键添加一个字段。由于这很愚蠢,我认为您需要删除 Jackson :(。但我可以建议一个不同的库。
我不知道 Newsletter 中的键/值对,它们是动态的,不是固定的,每时每刻都在变化。【参考方案3】:
以下是 JSON 到 Java 转换的工作原理
第一步是创建保存 JSON 数据的 Java 类。你已经有了一个。看一下json。您创建一个 Preferences
对象来保存
整个json。 json 包含一个 newsletter
元素。你
在Preferences
对象中创建Newsletter
类型的Java 对象。
创建org.codehaus.jackson.map.ObjectMapper
类的实例。这是课
将 JSON 映射到 Java 对象。
ObjectMapper mapper = new ObjectMapper();
然后使用ObjectMapper
的readValue
方法读取。此方法有多个版本,您可以使用采用
String
。但是,有些方法可以从 URL
、File
、InputStream
、String
或 ByteArray
读取。
ObjectMapper
缓存序列化器和反序列化器,因此最好将ObjectMapper
实例用于多个
转化
如果您有 String
,则将其传递给 Jackson 以将其转换为 Java 对象。
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
【讨论】:
如前所述,它是一个“sn-p”,是更大 json 的一部分。 感谢您提供详细信息。我知道他们。但是如何在不更改 Json 的情况下解决我的问题,因为我无法更改它。就像汤姆在他的回答中提到的那样,我更喜欢当前的抽象关系 Preference -> Newsletter。 如果你知道,为什么要问它?如果类没有必需的属性,则从 json 反序列化将不起作用,并且 json 应该具有反序列化器工作的有效格式。 我想为这个答案投票,因为当我在寻找解决方案时,它对我有帮助 【参考方案4】:我想我成功了。 我用
@JsonValue 创建时事通讯的 JSON 部分。 @JsonCreator 使用地图创建 NewsLetter 实例 包含键/值对。 @JsonTypeInfo 指示使用哪个实现来创建 NewsLetter 实例。使用上面的 json sn-p ,测试运行良好,双向。 在代码中(Preferences 类与原始问题中列出的相同):
@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,所以我希望它是正确的。我会用谷歌搜索更多的例子。感谢所有反馈。
【讨论】:
虽然这看起来没问题,但我认为这是矫枉过正。还有更简单的方法。 对不起。当我看到peeskillet的答案时,我将发布答案。这是最好的方法。以上是关于使用杰克逊数据绑定跳过嵌套字段?的主要内容,如果未能解决你的问题,请参考以下文章