Десериализовать элементы XML в карту Java
Я пытаюсь десериализовать XML-файл в объекты Java с использованием simpleXML. Все работало нормально, пока мне не пришлось использовать ElementMap. Это часть моего файла XML:
<piece composer="Me" date="Yesterday" title="Important work">
<editions>
<edition name="My Name" date="2015" description="A single page">
<pages>
<page fileName="page01.jpg" />
</pages>
</edition>
<edition name="My Name again" date="2015" description="Another single page">
<pages>
<page fileName="page01.jpg" />
</pages>
</edition>
</editions>
<layers>
<layer name="Annotations" color="#FF0000" description="Empty" id="anno" />
<layer name="Information" color="#00FF00" description="Empty" id="info" />
</layers>
</piece>
Соответствующий класс Java выглядит следующим образом:
@Root
public class Piece {
@Attribute
private String composer;
@Attribute
private String title;
@Attribute
private String date;
@ElementList
private List<Edition> editions;
@ElementMap(key = "id", attribute = true) // Relevant
private Map<String, Layer> layers; // Lines
public static Piece loadFromAsset(Context context, String fileName) throws Exception {
Serializer serial = new Persister();
return serial.read(Piece.class, context.getResources().getAssets().open(fileName));
}
}
И, наконец, класс слоев:
public class Layer {
@Attribute
private String id;
@Attribute
private String name;
@Attribute
private String description;
@Attribute
private String color;
}
После загрузки файла XML карта содержит оба ключа, но оба они указывают на null
вместо реальных объектов слоя.
1 ответ
Я не пользователь SimpleFrawmerow, поэтому я не гарантирую, что он будет работать, но вы, вероятно, должны указать больше атрибутов сопоставлений ElementMap:
@ElementMap(key = "id", attribute = true, keyType=String.class
,valueType=Layer.class)
Если это не помогает, вы должны сами обработать список для преобразования карты. Проблема в том, что ваш xml на самом деле содержит список элементов Layer вместо карты (нет пар элементов ключ / значение).
Вы можете добавить к вашему классу Piece дополнительное поле, чтобы сохранить этот список и построить поле карты, используя его:
class Piece {
...
@ElementList //only for solution B, for a do not use this annotation
List<Layer> layers;
@Transient //importatnt as it does not go into xml directly
private Map<String, Layer> layersMap;
}
Сейчас есть два подхода:
A) Используйте методы получения и установки для списка слоев, а внутри методов создайте содержимое карты списка по мере необходимости:
@ElementList
public void setLayers(Collection<Layer> layers) {
layersMap = new HashMap<>();
for (Layer l : layers) layersMap.put(l.getId(),l);
}
@ElementList
public Collection<Layer> getLayers() {
return layersMap.values();
}
B) использовать механизм жизненного цикла сериализации (аннотации Persist и Commit). В одном методе, вызываемом перед сериализацией, вы создаете содержимое списка с помощью карты, а в другом вы заполняете карту, используя значения списка. Лично я предпочитаю это методам получения / установки, так как вы можете скрыть элемент 'layer'.
class Piece {
....
@Commit
public void prepareMap() {
layersMap = new HashMap<>();
for (Layer l : layers) layersMap.put(l.getId(),l);
}
@Persist
public void prepareList() {
layers = new ArrayList<>();
layers.addAll(layersMap.values());
}
}
Наконец, вы можете использовать Конвертер, чтобы полностью контролировать процесс, но я считаю, что вышесказанное проще и менее многословно.