2015-04-06 3 views
0

Я хочу сериализовать класс с помощью xstream с помощью метода addImplicitMap, доступного в нем. Класс будет выглядеть следующим образом:Как использовать метод 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); 
} 

Я попытался как:

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> 

Пожалуйста, помогите мне ...

ответ

4

TLDR: addImplicitMap не позволит удалить <mapList> элемент контейнера и entry элементы и переименовывать элементы ключ/значение всех в один (пример 1 и 2). Для большего контроля над наименованием элементов на картах используйте NamedMapConverter (пример 3). Чтобы иметь возможность делать все сразу, вам понадобится пользовательская реализация Converter (пример 4).


Длинный ответ:

Там не кажется, хорошее объяснение на SO, что addImplicitMap делает, и это тоже не очень понятно, в их документации, поэтому я буду пытаться объяснить Вот.

addImplicitMap удалит элемент контейнера, но при его использовании он не позволит вам удалить элемент <entry> или переименовать элементы ключа/значения. Например: (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 он воссоздает ключи карты, используя указанное поле объектов значения, поэтому ключ карты должен быть сохранен в объекте значения карты.

Например, мы можем сделать значение карты это 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"); 

Выходов:

<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 (есть хороший учебник 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

+0

Спасибо за ваш ответ , Ваш ответ поддержан – Samraan