3

Map を XML 要素の属性にマーシャリング/アンマーシャリングしたいと考えています。次のような例を見てきました。

<map>
<entry key="key1">value1</entry>
<entry key="key2">value2</entry>
</map>

私が本当に欲しいのは:

<map key1="value1" key2="value2"/>

複雑な値はなく、XML 属性として合法的に表現できると仮定してください。また、キーのセットは実行時までわからないため、これを一般的に記述しようとしています。

これについてどうすればいいですか?私は XmlJavaTypeAdapter に精通しています。

エントリのリストを含む MyMap を作成することを考えましたが、これでは希望する出力が得られません。

4

2 に答える 2

1

コメントでほのめかしたように、これは JAXB だけでは実現できません。JAXB仕様(JSR 222)では、次のように述べています。

すべてのアプリケーション シナリオで、スキーマの Java オブジェクト レベルのバインディングを作成します。

つまり、バインディングのスコープは、静的なスキーマのスコープと同じです。JAXB バインディングは、コードを再コンパイルせずに変更することを意図していません。いくつかの例外があります。たとえばxs:anyAttribute、仕様のセクション 6.9 で説明されていますが、の使用を示唆する回答に投票しなかったため、@XmlAnyAttributeおそらく制限を受け入れたくないでしょう。たとえばQName、マップにキーしかありません。 .

JAXB でやりたいことをするのは本当に悪い考えだと確信していただければ幸いですが、参考までに、マーシャリング後にドキュメントを変更して必要な構造にする例を以下に示します。これをコピーして 1 つのファイルに貼り付け、Java 7 でコンパイルできます。出力は次のようになります。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<mapExample>
  <map France="Paris" Japan="Tokyo"/>
</mapExample>

私のコードは、他の方向が同等であるマーシャリングのみを示しています。

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

@XmlRootElement
class MapExample {
  @XmlJavaTypeAdapter(MapXmlAdapter.class)
  @XmlElement(name="map")
  private Map<String, String> data = new HashMap<>();

  public static void main(String[] args) throws Exception {
    MapExample example = new MapExample();
    example.data.put("France", "Paris");
    example.data.put("Japan", "Tokyo");

    JAXBContext context = JAXBContext.newInstance(MapExample.class);
    Marshaller marshaller = context.createMarshaller();
    DOMResult result = new DOMResult();
    marshaller.marshal(example, result);

    XPathFactory factory = XPathFactory.newInstance();
    XPath xpath = factory.newXPath();

    Document document = (Document)result.getNode();
    XPathExpression expression = xpath.compile("//map/entry");
    NodeList nodes = (NodeList)expression.evaluate(document, XPathConstants.NODESET);

    expression = xpath.compile("//map");
    Node oldMap = (Node)expression.evaluate(document, XPathConstants.NODE);    
    Element newMap = document.createElement("map");

    for (int index = 0; index < nodes.getLength(); index++) {
      Element element = (Element)nodes.item(index);
      newMap.setAttribute(element.getAttribute("key"), 
          element.getAttribute("value"));
    }

    expression = xpath.compile("//map/..");
    Node parent = (Node)expression.evaluate(document, XPathConstants.NODE);    
    parent.replaceChild(newMap, oldMap);

    TransformerFactory.newInstance().newTransformer().
      transform(new DOMSource(document), new StreamResult(System.out));
  }
}

class MapXmlAdapter extends XmlAdapter<MyMap, Map<String, String>> {
  @Override
  public Map<String, String> unmarshal(MyMap value) throws Exception {
    throw new UnsupportedOperationException();
  }

  @Override
  public MyMap marshal(Map<String, String> value) throws Exception {
    MyMap map = new MyMap();
    map.entries = new ArrayList<MyEntry>();
    for (String key : value.keySet()) {
      MyEntry entry = new MyEntry();
      entry.key = key;
      entry.value = value.get(key);
      map.entries.add(entry);
    }
    return map;
  }
}

class MyMap {
  @XmlElement(name="entry")
  public List<MyEntry> entries;
}

class MyEntry {
  @XmlAttribute
  public String key;

  @XmlAttribute
  public String value;
}
于 2012-10-15T18:08:10.987 に答える
-1

これはの使用のように聞こえます@XmlAnyAttribute。そのアノテーションをに配置するMap<QName, Object>と、他のアノテーションによって明示的にバインドされていないすべての属性がそのマップに収集されます。

@XmlRootElement
public class Example {
  @XmlElement(name = "map")
  @XmlJavaTypeAdapter(MapAdapter.class)
  private Map<String, String> map;
}

class MapAdapter extends XmlAdapter<MapWrapper, Map<String, String>> {
  @Override
  public Map<String, String> unmarshal(MapWrapper value) throws Exception {
    if(value == null || value.attributes == null) return null;

    Map<String, String> map = new HashMap<String, String>();
    for(Map.Entry<QName, Object> entry : value.attributes.entrySet()) {
      map.put(entry.getKey().getLocalPart(), entry.getValue().toString());
    }
    return map;
  }

  @Override
  public MapWrapper marshal(Map<String, String> map) throws Exception {
    if(map == null) return null;

    MapWrapper w = new MapWrapper();
    w.attributes = new HashMap<QName, Object>();
    for (Map.Entry<String, String> entry : map.entrySet()) {
      w.attributes.put(new QName(entry.getKey()), entry.getValue());
    }
    return w;
  }

}

class MapWrapper {
  @XmlAnyAttribute
  public Map<QName, Object> attributes;
}
于 2012-10-15T19:25:34.840 に答える