如果你想差异化null
最通用的方法将使用来自缺失字段的值字段Map
or JsonNode
代替POJO
. POJO
类具有恒定的结构,Map
or 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);
}
}
现在看一些例子,我们测试什么JSON
将生成为empty
, null
和缺失的节点。
Test 0-0
Input XML
:
<Root>
<a>A</a>
<b>1</b>
<c>
<c1>Rick</c1>
<c2>58</c2>
</c>
</Root>
Result JSON
is:
{"a":"A","b":"1","c":{"c1":"Rick","c2":"58"}}
Test 0-1
Input XML
:
<Root>
<a>A</a>
<c>
<c1>Rick</c1>
<c2/>
</c>
</Root>
Output JSON
:
{"a":"A","c":{"c1":"Rick","c2":null}}
Test 0-2
Input XML
:
<Root>
<c/>
</Root>
Output JSON
:
{"c":null}
这种简单快速的解决方案的最大问题是我们丢失了原语的类型信息。例如,如果b
is 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
算法如下:
- 将 JSON 读取为
Map
or JsonNode
- 查找所有字段名称
- Create
FilterProvider
找到的名称 - 请注意过滤器已注册allowedFields
名称,与中使用的相同@JsonFilter
注解。
- Convert
Map
to POJO
用于类型强制。
- Write
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;
}
}
Test 1-0
Input XML
:
<Root>
<a>A</a>
<b>1</b>
<c>
<c1>Rick</c1>
<c2>58</c2>
</c>
</Root>
Output JSON
:
{"a":"A","b":1,"c":{"c1":"Rick","c2":58}}
Test 1-1
Input XML
:
<Root>
<b>1</b>
<c>
<c2/>
</c>
</Root>
Output JSON
:
{"b":1,"c":{"c2":null}}
Test 1-2
Input XML
:
<Root>
<c/>
</Root>
Output JSON
:
{"c":null}
经过所有这些测试,我们看到动态检查字段是否为null
, empty
or absent
这不是一件容易的事。即便如此,上述 2 个解决方案适用于简单模型,您应该测试它们以获取想要生成的所有响应。当模型很复杂并且包含许多复杂的注释时,例如:@JsonTypeInfo
, @JsonSubTypes
on Jackson
侧面或@XmlElementWrapper
, @XmlAnyElement
on JAXB
这使得这项任务很难执行。
我认为你的例子中最好的解决方案是使用@JsonInclude(NON_NULL)
将所有设置字段发送给客户端XML
side. null
and absent
应在客户端进行相同的处理。业务逻辑不应依赖于事实字段设置为null
or absent
in JSON
有效负载。
也可以看看:
- JSON 中空与 null 的约定是什么? https://stackoverflow.com/questions/9619852/what-is-the-convention-in-json-for-empty-vs-null
- 在 JSON 中表示 null https://stackoverflow.com/questions/21120999/representing-null-in-json
- 使用 Lombok 和 MapStruct 实现 PATCH 方法 https://stackoverflow.com/questions/74886852/how-to-deserialize-json-and-distinguish-null-from-the-absence-of-a-value