如何使用Xstream的addImplicitMap方法?

Sam*_*aan 0 java xml xstream

我想借助于其中提供的addImplicitMap方法使用xstream序列化该类.类看起来像:

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);
}
Run Code Online (Sandbox Code Playgroud)

我尝试过:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

但我没有得到正确的输出.我的预期输出如下:

<?xml version="1.0" ?>
<MapTest>
 <id>Started</id>
 <name>Started</name>
</MapTest>
Run Code Online (Sandbox Code Playgroud)

请帮帮我...

Pho*_*nix 5

TLDR:addImplicitMap不允许您删除<mapList>容器元素和entry元素,并一次重命名键/值元素(例1和2).要更好地控制地图中元素的命名,请使用NamedMapConverter(示例3).为了能够一次完成所有事情,您将需要一个自定义Converter实现(示例4).


答案很长:

似乎没有一个很好的解释是什么addImplicitMap做的,并且在他们的文档中也不是很清楚,所以我将尝试在这里解释.

addImplicitMap将删除容器元素,但是,当这样使用时,它将不允许您删除<entry>元素或重命名键/值元素.例如:(1)

stream.addImplicitMap(MapTest.class, "mapList", "s", Map.Entry.class, null);
Run Code Online (Sandbox Code Playgroud)

结果是:

<MapTest>
  <s>
    <string>ID01</string>
    <string>Jack</string>
  </s>
  <s>
    <string>ID02</string>
    <string>Neil</string>
  </s>
</MapTest>
Run Code Online (Sandbox Code Playgroud)

或者,addImplicitMap允许您在写入时存储地图的值(省略容器元素和关键元素).读取XML时,它使用值对象的指定字段重新创建映射键,因此映射键必须存储在映射值对象中.

例如,我们可以使地图的值成为Service具有ID属性的对象,并使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");
Run Code Online (Sandbox Code Playgroud)

输出:

<MapTest>
  <s>
    <id>ID01</id>
    <name>Jack</name>
  </s>
  <s>
    <id>ID02</id>
    <name>Neil</name>
  </s>
</MapTest>
Run Code Online (Sandbox Code Playgroud)

请注意,这里每个<s>元素实际上是序列化Service对象,而不是Map.Entry对象.


这仍然不能解决你的问题(我们也必须改变地图中的内容),所以我们可以试试namedMapConverter.如果我们回到使用<String, String>您原来的地图,我们可以使用:(3)

stream.registerConverter(new NamedMapConverter(stream.getMapper(),
                null, "id", String.class, "name", String.class));
Run Code Online (Sandbox Code Playgroud)

这给出了输出:

<MapTest>
  <serviceMap>
    <id>ID01</id>
    <name>Jack</name>
    <id>ID02</id>
    <name>Neil</name>
  </serviceMap>
</MapTest>
Run Code Online (Sandbox Code Playgroud)

还没完全正确的,我不相信你可以做的更好,但并不实现自定义的Converter类(有一个很好的教程在这里).所以我们添加行(而不是NamedMapConverter):

stream.registerConverter(new MapTestConverter());
Run Code Online (Sandbox Code Playgroud)

并使用自定义转换器实现:(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;
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,我们得到了预期的结果:

<MapTest>
  <id>ID01</id>
  <name>Jack</name>
  <id>ID02</id>
  <name>Neil</name>
</MapTest>
Run Code Online (Sandbox Code Playgroud)

(对不起,我没有足够的代表将以下内容变成链接)

完整代码addImplicitMap例如:pastebin.com/MYiAde3m

完整代码namedMapConverter例如:pastebin.com/kVChup5x

完整代码Converter例如:pastebin.com/vUTwaHkk