Java 如何在jackson库中区分空值字段和缺少字段
我们正在使用一个API,该API提供xml字段。我们必须为消费者将xml转换为json。我们需要以XML的形式显示我们得到的内容,并且只显示那些字段Java 如何在jackson库中区分空值字段和缺少字段,java,json,xml,jackson,Java,Json,Xml,Jackson,我们正在使用一个API,该API提供xml字段。我们必须为消费者将xml转换为json。我们需要以XML的形式显示我们得到的内容,并且只显示那些字段 如果字段存在且存在值,则显示它 如果字段不存在,则不显示它 如果字段存在且为空/无值,则按原样显示该字段 我看到的是一般注释 @JsonInclude(非空)可用于排除空值。我无法使用此选项,因为我仍希望在json中看到空值字段 @JsonInclude(非不存在)可用于排除空值和“不存在”的值。我无法使用此选项,因为我仍然希望看到json中的空字
@JsonInclude(非空)
可用于排除空值。我无法使用此选项,因为我仍希望在json中看到空值字段
@JsonInclude(非不存在)
可用于排除空值和“不存在”的值。我无法使用此选项,因为我仍然希望看到json中的空字段和空字段。与JsonInclude相同(非空)
所以我的问题是,如果我不指定这些属性中的任何一个,我能实现我想要的吗?换句话说,如果我没有指定其中任何一个,那么jackson的行为就是显示所有在dynamic sense上有空值的字段?我主要关心的是这里的动态响应。对于每个请求,字段可能存在或不存在。我们必须在json中显示我们在XML中接收到的确切内容如果您想区分
null
值字段和不存在的字段,最通用的方法是使用Map
或JsonNode
而不是POJO
POJO
类具有常量结构,Map
或JsonNode
具有动态-仅包含实际放在那里的内容。让我们创建一个简单的应用程序,从文件中读取XML
有效负载,并创建JSON
响应:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.File;
import java.util.Map;
public class JsonApp {
public static void main(String[] args) throws Exception {
File xmlFile = new File("./resource/test.xml").getAbsoluteFile();
XmlMapper xmlMapper = new XmlMapper();
Map map = xmlMapper.readValue(xmlFile, Map.class);
ObjectMapper jsonMapper = new ObjectMapper();
String json = jsonMapper.writeValueAsString(map);
System.out.println(json);
}
}
现在来看一些示例,其中我们测试将为empty
、null
和缺少的节点生成什么JSON
测试0-0
输入XML
:
<Root>
<a>A</a>
<b>1</b>
<c>
<c1>Rick</c1>
<c2>58</c2>
</c>
</Root>
<Root>
<a>A</a>
<c>
<c1>Rick</c1>
<c2/>
</c>
</Root>
<Root>
<c/>
</Root>
<Root>
<a>A</a>
<b>1</b>
<c>
<c1>Rick</c1>
<c2>58</c2>
</c>
</Root>
<Root>
<b>1</b>
<c>
<c2/>
</c>
</Root>
<Root>
<c/>
</Root>
测试0-1
输入XML
:
<Root>
<a>A</a>
<b>1</b>
<c>
<c1>Rick</c1>
<c2>58</c2>
</c>
</Root>
<Root>
<a>A</a>
<c>
<c1>Rick</c1>
<c2/>
</c>
</Root>
<Root>
<c/>
</Root>
<Root>
<a>A</a>
<b>1</b>
<c>
<c1>Rick</c1>
<c2>58</c2>
</c>
</Root>
<Root>
<b>1</b>
<c>
<c2/>
</c>
</Root>
<Root>
<c/>
</Root>
测试0-2
输入XML
:
<Root>
<a>A</a>
<b>1</b>
<c>
<c1>Rick</c1>
<c2>58</c2>
</c>
</Root>
<Root>
<a>A</a>
<c>
<c1>Rick</c1>
<c2/>
</c>
</Root>
<Root>
<c/>
</Root>
<Root>
<a>A</a>
<b>1</b>
<c>
<c1>Rick</c1>
<c2>58</c2>
</c>
</Root>
<Root>
<b>1</b>
<c>
<c2/>
</c>
</Root>
<Root>
<c/>
</Root>
这种简单而快速的解决方案的最大问题是我们丢失了原语的类型信息。例如,如果b
是Integer
,我们应该在JSON
中返回它作为没有引号的数字原语:“
字符。要解决这个问题,我们应该使用POJO
模型,它允许我们找到所有必需的类型。让我们为我们的示例创建POJO
模型:
@JsonFilter("allowedFields")
class Root {
private String a;
private Integer b;
private C c;
// getters, setters
}
@JsonFilter("allowedFields")
class C {
private String c1;
private Integer c2;
// getters, setters
}
我们需要将简单的XML->Map->JSON
算法更改为以下算法:
Map
或JsonNode
FilterProvider
——请注意,过滤器注册到allowedFields
name,与@JsonFilter
注释中使用的相同Map
转换为POJO
进行类型强制POJO
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.File;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
public class JsonApp {
public static void main(String[] args) throws Exception {
File xmlFile = new File("./resource/test.xml").getAbsoluteFile();
NodesWalker walker = new NodesWalker();
XmlMapper xmlMapper = new XmlMapper();
JsonNode root = xmlMapper.readValue(xmlFile, JsonNode.class);
Set<String> names = walker.findAllNames(root);
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("allowedFields", SimpleBeanPropertyFilter.filterOutAllExcept(names));
ObjectMapper jsonMapper = new ObjectMapper();
jsonMapper.setFilterProvider(filterProvider);
Root rootConverted = jsonMapper.convertValue(root, Root.class);
String json = jsonMapper.writeValueAsString(rootConverted);
System.out.println(json);
}
}
class NodesWalker {
public Set<String> findAllNames(JsonNode node) {
Set<String> names = new HashSet<>();
LinkedList<JsonNode> nodes = new LinkedList<>();
nodes.add(node);
while (nodes.size() > 0) {
JsonNode first = nodes.removeFirst();
if (first.isObject()) {
ObjectNode objectNode = (ObjectNode) first;
objectNode.fields().forEachRemaining(e -> {
names.add(e.getKey());
JsonNode value = e.getValue();
if (value.isObject() || value.isArray()) {
nodes.add(value);
}
});
} else if (first.isArray()) {
ArrayNode arrayNode = (ArrayNode) first;
arrayNode.elements().forEachRemaining(e -> {
if (e.isObject() || e.isArray()) {
nodes.add(e);
}
});
}
}
return names;
}
}
输出JSON
:
{"a":"A","c":{"c1":"Rick","c2":null}}
{"c":null}
{"a":"A","b":1,"c":{"c1":"Rick","c2":58}}
{"b":1,"c":{"c2":null}}
{"c":null}
测试1-1
输入XML
:
<Root>
<a>A</a>
<b>1</b>
<c>
<c1>Rick</c1>
<c2>58</c2>
</c>
</Root>
<Root>
<a>A</a>
<c>
<c1>Rick</c1>
<c2/>
</c>
</Root>
<Root>
<c/>
</Root>
<Root>
<a>A</a>
<b>1</b>
<c>
<c1>Rick</c1>
<c2>58</c2>
</c>
</Root>
<Root>
<b>1</b>
<c>
<c2/>
</c>
</Root>
<Root>
<c/>
</Root>
测试1-2
输入XML
:
<Root>
<a>A</a>
<b>1</b>
<c>
<c1>Rick</c1>
<c2>58</c2>
</c>
</Root>
<Root>
<a>A</a>
<c>
<c1>Rick</c1>
<c2/>
</c>
</Root>
<Root>
<c/>
</Root>
<Root>
<a>A</a>
<b>1</b>
<c>
<c1>Rick</c1>
<c2>58</c2>
</c>
</Root>
<Root>
<b>1</b>
<c>
<c2/>
</c>
</Root>
<Root>
<c/>
</Root>
在所有这些测试之后,我们发现动态检查字段是否为null
,为空
或不存在
并不是一项容易的任务。即使如此,上述两种解决方案对简单模型都有效,您应该测试它们以获得所有想要生成的响应。当模型复杂且包含许多复杂注释时,例如:@JsonTypeInfo
,@JsonSubTypes
在Jackson
侧或@xmlementwrapper
,@xmlanyement
在JAXB
侧,这使得这项任务很难实现
我认为在您的示例中,最好的解决方案是使用@JsonInclude(非空)
将XML
端的所有设置字段发送给客户端。null
和缺席
应在客户端得到相同的处理。业务逻辑不应依赖于JSON
有效负载中设置为null
或缺席
的事实字段
另见:
XML
转换为JSON
,这不是一项容易的任务。我们应该深入理解XML
中的empty
节点和JSON
中的empty
节点的含义。您可以创建一些规则,在X
、Y
、Z
情况下如何处理,并尝试创建自定义解决方案。