2012-10-15 28 views
3

我想將marshall/unmarshall映射到XML元素的屬性。我看到了這樣的例子:JAXB java.util.Map到鍵值對

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

我真正想要的是:

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

與我假設沒有複雜的價值觀,他們可以合法地被表示爲XML屬性。另外,我試圖一般性地寫這個,因爲直到運行時才知道這組鍵。

我該怎麼辦?我熟悉XmlJavaTypeAdapter。

我想過創建一個包含條目列表的MyMap,但是這不會得到我想要的輸出。

回答

1

就像我在我的評論中暗示的那樣,單靠JAXB是無法實現的。在JAXB規範(JSR 222)中,它表示:

在所有應用場景中,我們都創建了模式的Java對象級綁定。

這意味着綁定的範圍與模式的範圍相同,該範圍是靜態的。如果不重新編譯代碼,JAXB綁定不會被更改。有一些例外,例如對於xs:anyAttribute這在本規範第6.9節中討論過,但由於您沒有投票給出建議使用@XmlAnyAttribute的答案,您可能不想忍受這些限制 - 例如,在您的地圖中只有QName鍵。

我希望你確信,用JAXB做你想做的事情是一個非常糟糕的主意,但僅供參考,下面是一個示例,它在編組之後修改文檔以將其引入到所需的結構中。您可以複製並粘貼到一個單一的文件,並與Java 7編譯它的輸出看起來就像這樣:

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

我的代碼只顯示marshalilng另一個方向是相同的:

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; 
} 
+0

你能舉個例子嗎?在我看來,Javadoc解釋瞭如何註釋對象以獲得OP *不需要的XML結構... –

+0

不是我正在尋找的結構。我希望Map中的所有條目都是鍵值對。 – bmauter

+0

Java地圖對象中的條目不受此影響,例如他們仍然實現'java.util.Map.Entry'。 'MyEntry'僅在編組期間使用。或者,你的意思是「不是我正在尋找的結構」的XML輸出?還是你想讓我擴展這個例子來處理更多的類型,而不僅僅是''? – Tilo

3

有一個nice article,它解釋瞭如何使用XMLAdapter將自定義XML映射到Java地圖。我會建議你經歷一次。

爲了方便訪問,我將張貼在這裏的有用的代碼:

XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<profile> 
    <messages> 
     <message id="1"> 
      <subject>hi</subject> 
      <body>wat's up mike.. r u gonna catch us tonight?</body> 
     </message> 
     <message id="2"> 
      <subject>re:hi</subject> 
      <body>My apologies, forgot to tell ya, I'm out of town!!!</body> 
     </message> 
    </messages> 
</profile> 

JAXB註釋消息類:

public class Message { 
    @XmlAttribute 
    private String id; 
    @XmlElement 
    private String subject; 
    @XmlElement 
    private String body; 
} 

檔案類:

@XmlRootElement(name="profile") 
public class Profile { 
    @XmlElement 
    @XmlJavaTypeAdapter(MessageAdapter.class) 
    private HashMap<String, Message> messages; 

    public Profile(){} 
    public Profile(HashMap<String, Message> b){ 
     messages = b; 
    } 
} 

消息類:

public class Messages { 
    @XmlElement(name="message") 
    public Message[] messages; 
} 

MessageAdaptor:

public class MessageAdapter extends XmlAdapter<Messages,Map<String, Message>> { 
    @Override 
    public Map<String, Message> unmarshal(Messages value){ 
     Map<String, Message> map = new HashMap<String, Message>(); 
     for(Message msg : value.messages) 
      map.put(msg.getId(), msg); 
     return map; 
    } 

    @Override 
    public Messages marshal(Map<String, Message> map){ 
     Messages msgCont = new Messages(); 
     Collection<Message> msgs = map.values(); 
     msgCont.messages = msgs.toArray(new Message[msgs.size()]); 
     return msgCont; 
    } 
} 

最後的XMLAdaptorTest

public class XmlAdapterTest extends TestCase{ 

    public void testAdapter() throws Exception { 
     InputStream is = this.getClass().getClassLoader().getResourceAsStream("profile.xml"); 
     if (is != null) { 
      JAXBContext jc; 
      try { 
       //test unmarshaling 
       jc = JAXBContext.newInstance(Profile.class.getPackage().getName()); 
       Unmarshaller u = jc.createUnmarshaller(); 
       Profile profile = (Profile) u.unmarshal(is); 
       assertNotNull(profile.getMessages()); 
       assertEquals(2, profile.getMessages().size()); 

       //test marshaling 
       Marshaller marshaller=jc.createMarshaller(); 
       File xmlDocument = new File("output.xml"); 
       marshaller.marshal(profile, new FileOutputStream(xmlDocument)); 
       assertTrue(xmlDocument.length() > 0); 
       xmlDocument.delete(); 
      } catch (JAXBException e) { 
       e.printStackTrace(); 
       fail(); 
      } 
     } 
    } 
} 
+0

這看起來像jaxb的通用howto。我知道如何做jaxb。這是如何回答我的問題的? – bmauter

-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; 
} 
+0

我要試試這個。 – bmauter

+1

不錯的包裝 - 但它創建的屬性節點不是真正的節點。 –