将 XML 元素反序列化为 Java 映射

Deserialize XML Elements to Java Map

我正在尝试使用简单XML 将XML 文件反序列化为Java 对象。一切正常,直到我不得不使用 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>

对应的JavaClass是这样的:

@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));
    }
}

最后层 Class:

public class Layer {
    @Attribute
    private String id;
    @Attribute
    private String name;
    @Attribute
    private String description;
    @Attribute
    private String color;
}

加载 XML 文件后,地图包含两个键,但它们都指向 null 而不是实际的图层对象。

我不是 SimpleFrawmerow 用户,所以我不保证它会起作用,但您可能应该指定 ElementMap 映射的更多属性:

@ElementMap(key = "id", attribute = true, keyType=String.class
            ,valueType=Layer.class)

如果没有帮助,您必须自己处理列表到地图的转换。问题是您的 xml 实际上包含 Layer 元素列表而不是地图(没有成对的键/值元素)。

您可以添加到您的 Piece class 额外字段来存储该列表并使用它构建地图字段:

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) 对图层列表使用getters和setters,方法内部根据需要构建列表地图内容:

   @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 注解)。在序列化之前调用的一种方法中,您使用地图创建列表内容,而在另一种方法中,您使用列表值填充地图。我个人更喜欢它而不是 getters/setters,因为你可以隐藏 'layers' 元素。

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());
} 
} 

你终于可以使用 Converter 来完全控制这个过程了,但我相信上面的方法更简单也更简洁。