如何使用Xstream的addImplicitMap方法?
How to use addImplicitMap method of Xstream?
我想在其中可用的 addImplicitMap 方法的帮助下使用 xstream 序列化 class。 Class 看起来像:
class MapTest{
private Map<String, String> mapList;
public MapTest() {
mapList= new HashMap<String, String>();
}
public void setServicesHealth(String id, String name) {
map.put(id, name);
}
我试过:
class MapTestMain{
public static void main(String args[]){
MapTest services = new MapTest();
services.setServicesHealth("ID01", "Jack");
services.setServicesHealth("ID02", "Neil);
XStream stream = new XStream(new StaxDriver());
stream.alias("MapTest", MapTest.class);
stream.addImplicitMap(MapTest.class, "map", "id", String.class, "name");
String xmlStr = stream.toXML(services);
System.out.println(xmlStr);
}
}
但我没有得到正确的输出。我的预期输出如下:
<?xml version="1.0" ?>
<MapTest>
<id>Started</id>
<name>Started</name>
</MapTest>
请帮帮我...
TLDR:addImplicitMap
不允许您一次性删除 <mapList>
容器元素和 entry
元素并重命名 key/value 元素(示例 1 和2).要更好地控制地图中元素的命名,请使用 NamedMapConverter
(示例 3)。为了能够一次完成所有事情,您将需要一个自定义 Converter
实现(示例 4)。
长答案:
对于 addImplicitMap
的作用似乎没有很好的解释,而且在他们的文档中也不是很清楚,所以我将在这里尝试解释一下。
addImplicitMap
将删除容器元素,但是,当这样使用时,它不会让您删除 <entry>
元素或重命名 key/value 元素。例如:(1)
stream.addImplicitMap(MapTest.class, "mapList", "s", Map.Entry.class, null);
结果:
<MapTest>
<s>
<string>ID01</string>
<string>Jack</string>
</s>
<s>
<string>ID02</string>
<string>Neil</string>
</s>
</MapTest>
或者,addImplicitMap
允许您在写入时仅存储映射的值(省略容器元素和键元素)。读取 XML 时,它会使用值对象的指定字段重新创建映射键,因此 映射键必须存储在映射值对象中 .
例如,我们可以使地图的值成为具有 ID 属性的 Service
对象,并使 Xstream 仅存储这些值并从中重新创建地图:(2 )
private Map<String, Service> serviceMap; // modified in MapTest
static class Service {
private String id, name;
public Service(String id, String name) {
this.id = id;
this.name = name;
}
}
MapTest services = new MapTest();
services.setService("ID01", new Service("ID01", "Jack"));
services.setService("ID02", new Service("ID02", "Neil"));
stream.addImplicitMap(MapTest.class, "serviceMap", "s", Service.class, "id");
输出:
<MapTest>
<s>
<id>ID01</id>
<name>Jack</name>
</s>
<s>
<id>ID02</id>
<name>Neil</name>
</s>
</MapTest>
请注意,这里每个 <s>
元素实际上是序列化的 Service
对象,而不是 Map.Entry
对象。
这仍然不能完全解决您的问题(我们还必须更改地图中的内容),因此我们可以尝试 namedMapConverter
。如果我们回到原来使用 <String, String>
的映射,我们可以使用:(3)
stream.registerConverter(new NamedMapConverter(stream.getMapper(),
null, "id", String.class, "name", String.class));
给出输出:
<MapTest>
<serviceMap>
<id>ID01</id>
<name>Jack</name>
<id>ID02</id>
<name>Neil</name>
</serviceMap>
</MapTest>
仍然不太正确,我不相信如果不实施自定义 Converter
class(有一个很好的教程 here),您可以使它变得更好。所以我们添加行(而不是 NamedMapConverter
):
stream.registerConverter(new MapTestConverter());
并使用自定义转换器实现:(4)
static class MapTestConverter implements Converter {
public boolean canConvert(Class type) {
return type.equals(MapTest.class);
}
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
MapTest mt = (MapTest) source;
for (Entry<String, String> e : mt.serviceMap.entrySet()) {
writer.startNode("id");
writer.setValue(e.getKey());
writer.endNode();
writer.startNode("name");
writer.setValue(e.getValue());
writer.endNode();
}
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
MapTest mt = new MapTest();
String id = null;
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("id".equals(reader.getNodeName())) {
if (id != null) { throw new RuntimeException("Malformed XML, ID was set twice: " + id); }
id = (String) context.convertAnother(mt, String.class);
} else if ("name".equals(reader.getNodeName())) {
String name = (String) context.convertAnother(mt, String.class);
if (id == null) { throw new RuntimeException("Malformed XML: Found name without ID: " + name); }
mt.serviceMap.put(id, name);
id = null;
}
reader.moveUp();
}
return mt;
}
}
终于得到了想要的结果:
<MapTest>
<id>ID01</id>
<name>Jack</name>
<id>ID02</id>
<name>Neil</name>
</MapTest>
(抱歉,我没有足够的代表将以下内容添加到链接中)
addImplicitMap
示例的完整代码:pastebin。com/MYiAde3m
namedMapConverter
示例的完整代码:pastebin。com/kVChup5x
Converter
示例的完整代码:pastebin。com/vUTwaHkk
我想在其中可用的 addImplicitMap 方法的帮助下使用 xstream 序列化 class。 Class 看起来像:
class MapTest{
private Map<String, String> mapList;
public MapTest() {
mapList= new HashMap<String, String>();
}
public void setServicesHealth(String id, String name) {
map.put(id, name);
}
我试过:
class MapTestMain{
public static void main(String args[]){
MapTest services = new MapTest();
services.setServicesHealth("ID01", "Jack");
services.setServicesHealth("ID02", "Neil);
XStream stream = new XStream(new StaxDriver());
stream.alias("MapTest", MapTest.class);
stream.addImplicitMap(MapTest.class, "map", "id", String.class, "name");
String xmlStr = stream.toXML(services);
System.out.println(xmlStr);
}
}
但我没有得到正确的输出。我的预期输出如下:
<?xml version="1.0" ?>
<MapTest>
<id>Started</id>
<name>Started</name>
</MapTest>
请帮帮我...
TLDR:addImplicitMap
不允许您一次性删除 <mapList>
容器元素和 entry
元素并重命名 key/value 元素(示例 1 和2).要更好地控制地图中元素的命名,请使用 NamedMapConverter
(示例 3)。为了能够一次完成所有事情,您将需要一个自定义 Converter
实现(示例 4)。
长答案:
对于 addImplicitMap
的作用似乎没有很好的解释,而且在他们的文档中也不是很清楚,所以我将在这里尝试解释一下。
addImplicitMap
将删除容器元素,但是,当这样使用时,它不会让您删除 <entry>
元素或重命名 key/value 元素。例如:(1)
stream.addImplicitMap(MapTest.class, "mapList", "s", Map.Entry.class, null);
结果:
<MapTest>
<s>
<string>ID01</string>
<string>Jack</string>
</s>
<s>
<string>ID02</string>
<string>Neil</string>
</s>
</MapTest>
或者,addImplicitMap
允许您在写入时仅存储映射的值(省略容器元素和键元素)。读取 XML 时,它会使用值对象的指定字段重新创建映射键,因此 映射键必须存储在映射值对象中 .
例如,我们可以使地图的值成为具有 ID 属性的 Service
对象,并使 Xstream 仅存储这些值并从中重新创建地图:(2 )
private Map<String, Service> serviceMap; // modified in MapTest
static class Service {
private String id, name;
public Service(String id, String name) {
this.id = id;
this.name = name;
}
}
MapTest services = new MapTest();
services.setService("ID01", new Service("ID01", "Jack"));
services.setService("ID02", new Service("ID02", "Neil"));
stream.addImplicitMap(MapTest.class, "serviceMap", "s", Service.class, "id");
输出:
<MapTest>
<s>
<id>ID01</id>
<name>Jack</name>
</s>
<s>
<id>ID02</id>
<name>Neil</name>
</s>
</MapTest>
请注意,这里每个 <s>
元素实际上是序列化的 Service
对象,而不是 Map.Entry
对象。
这仍然不能完全解决您的问题(我们还必须更改地图中的内容),因此我们可以尝试 namedMapConverter
。如果我们回到原来使用 <String, String>
的映射,我们可以使用:(3)
stream.registerConverter(new NamedMapConverter(stream.getMapper(),
null, "id", String.class, "name", String.class));
给出输出:
<MapTest>
<serviceMap>
<id>ID01</id>
<name>Jack</name>
<id>ID02</id>
<name>Neil</name>
</serviceMap>
</MapTest>
仍然不太正确,我不相信如果不实施自定义 Converter
class(有一个很好的教程 here),您可以使它变得更好。所以我们添加行(而不是 NamedMapConverter
):
stream.registerConverter(new MapTestConverter());
并使用自定义转换器实现:(4)
static class MapTestConverter implements Converter {
public boolean canConvert(Class type) {
return type.equals(MapTest.class);
}
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
MapTest mt = (MapTest) source;
for (Entry<String, String> e : mt.serviceMap.entrySet()) {
writer.startNode("id");
writer.setValue(e.getKey());
writer.endNode();
writer.startNode("name");
writer.setValue(e.getValue());
writer.endNode();
}
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
MapTest mt = new MapTest();
String id = null;
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("id".equals(reader.getNodeName())) {
if (id != null) { throw new RuntimeException("Malformed XML, ID was set twice: " + id); }
id = (String) context.convertAnother(mt, String.class);
} else if ("name".equals(reader.getNodeName())) {
String name = (String) context.convertAnother(mt, String.class);
if (id == null) { throw new RuntimeException("Malformed XML: Found name without ID: " + name); }
mt.serviceMap.put(id, name);
id = null;
}
reader.moveUp();
}
return mt;
}
}
终于得到了想要的结果:
<MapTest>
<id>ID01</id>
<name>Jack</name>
<id>ID02</id>
<name>Neil</name>
</MapTest>
(抱歉,我没有足够的代表将以下内容添加到链接中)
addImplicitMap
示例的完整代码:pastebin。com/MYiAde3m
namedMapConverter
示例的完整代码:pastebin。com/kVChup5x
Converter
示例的完整代码:pastebin。com/vUTwaHkk