[Java] JAXB: मार्शल कैसे <key> मान </ key> में मैप करें


Answers

ऐसा एक वैध कारण हो सकता है कि आप ऐसा क्यों करना चाहते हैं, लेकिन इस प्रकार की XML को आम तौर पर सर्वश्रेष्ठ से बचा जाता है। क्यूं कर? क्योंकि इसका मतलब है कि आपके नक्शे के XML तत्व आपके नक्शे की क्रम सामग्री पर निर्भर हैं। और जब से एक्सएमएल आमतौर पर एक बाहरी इंटरफ़ेस या इंटरफ़ेस परत के रूप में प्रयोग किया जाता है यह वांछनीय नहीं है। मुझे समझाने दो।

एक्सएमएल स्कीमा (एक्सएसडी) आपके XML दस्तावेज़ों के इंटरफ़ेस अनुबंध को परिभाषित करता है एक्सएसडी से कोड जनरेट करने में सक्षम होने के अलावा, जेएसीएबी कोड से आपके लिए एक्सएमएल स्कीमा भी उत्पन्न कर सकता है। यह आपको XSD में परिभाषित पूर्व-सहमति वाले संरचनाओं के लिए इंटरफ़ेस पर आदान-प्रदान डेटा को प्रतिबंधित करने देता है।

Map<String, String> लिए डिफ़ॉल्ट मामले में, जेनरेट XSD कई तत्वों को शामिल करने के लिए मानचित्र तत्व को प्रतिबंधित करेगा जिनमें से प्रत्येक में एक xs:string कुंजी और एक xs:string मान होना चाहिए। यह एक बहुत स्पष्ट इंटरफ़ेस अनुबंध है

आप जो वर्णन करते हैं वह है कि आप एक्सएमएल मानचित्र को ऐसे तत्वों को शामिल करना चाहते हैं जिनके नाम रनटाइम पर मानचित्र की सामग्री से निर्धारित किए जाएंगे। तब जेनरेट XSD केवल निर्दिष्ट कर सकता है कि नक्शा में तत्वों की एक सूची होनी चाहिए जिनके प्रकार समय संकलन पर अज्ञात हैं। ऐसा कुछ है जिसे आप इंटरफ़ेस अनुबंध को परिभाषित करते समय आम तौर पर बचना चाहिए।

इस मामले में सख्त अनुबंध प्राप्त करने के लिए, आपको स्ट्रैग के बजाय मानचित्र की कुंजी के रूप में एक एन्यूमरेटेड टाइप का उपयोग करना चाहिए। उदाहरण के लिए

public enum KeyType {
 KEY, KEY2;
}

@XmlJavaTypeAdapter(MapAdapter.class)
Map<KeyType , String> mapProperty;

इस तरह से जो चीजें आप एक्सएमएल में तत्व बनना चाहते हैं, उन्हें समय संकलन में जाना जाता है, इसलिए जेएसीबी को एक स्कीमा उत्पन्न करने में सक्षम होना चाहिए जो कि पूर्वनिर्धारित कुंजी कुंजी या कुंजी 2 में से एक का उपयोग कर तत्वों के लिए मानचित्र के तत्वों को सीमित कर देगा।

दूसरी तरफ, यदि आप डिफ़ॉल्ट जनरेटेड स्ट्रक्चर को सरल करना चाहते हैं

<map>
    <entry>
        <key>KEY</key>
        <value>VALUE</value>
    </entry>
    <entry>
        <key>KEY2</key>
        <value>VALUE2</value>
    </entry>
</map>

इस तरह से कुछ सरल करने के लिए

<map>
    <item key="KEY" value="VALUE"/>
    <item key="KEY2" value="VALUE2"/>
</map>

आप MapAdapter का उपयोग कर सकते हैं जो मैप एलेमेंट्स की एक सरणी को मानचित्र को निम्नानुसार बदलता है:

class MapElements {
    @XmlAttribute
    public String key;
    @XmlAttribute
    public String value;

    private MapElements() {
    } //Required by JAXB

    public MapElements(String key, String value) {
        this.key = key;
        this.value = value;
    }
}


public class MapAdapter extends XmlAdapter<MapElements[], Map<String, String>> {
    public MapAdapter() {
    }

    public MapElements[] marshal(Map<String, String> arg0) throws Exception {
        MapElements[] mapElements = new MapElements[arg0.size()];
        int i = 0;
        for (Map.Entry<String, String> entry : arg0.entrySet())
            mapElements[i++] = new MapElements(entry.getKey(), entry.getValue());

        return mapElements;
    }

    public Map<String, String> unmarshal(MapElements[] arg0) throws Exception {
        Map<String, String> r = new TreeMap<String, String>();
        for (MapElements mapelement : arg0)
            r.put(mapelement.key, mapelement.value);
        return r;
    }
}
Question

प्रश्न JAXB मानचित्र मार्शलिंग के बारे में है - इस तरह के उदाहरणों के बहुत सारे उदाहरण हैं, जैसे कि ढांचे की तरह एक मानचित्र को कैसे मारता है:

<map>
  <entry>
    <key> KEY </key>
    <value> VALUE </value>
  </entry>
  <entry>
    <key> KEY2 </key>
    <value> VALUE2 </value>
  </entry>
  <entry>
  ...
</map>

वास्तव में, यह मूल रूप से JAXB द्वारा समर्थित है मुझे क्या जरूरत है, हालांकि, वह एक्सएमएल है जहां कुंजी तत्व का नाम है, और मूल्य इसकी सामग्री है:

<map>
  <key> VALUE </key>
  <key2> VALUE2 </key2>
 ...
</map>

मैं अपने नक्शा एडाप्टर को जिस तरीके से इसे जेएएएसबी डेवलपर्स ( https://jaxb.dev.java.net/guide/Mapping_your_favorite_class.html ) की सिफारिश की है, उसे आवश्यकतानुसार लागू करने में सफल नहीं हुआ, वह - गतिशील विशेषता नाम :)

क्या इसके लिए कोई हल है?

पीएस। वर्तमान में मुझे कुंजी-वैल्यू जोड़े के प्रत्येक विशिष्ट सेट के लिए एक समर्पित कंटेनर वर्ग बनाना होगा जो मैं एक्सएमएल को मार्शल करना चाहता हूं- यह काम करता है, लेकिन मुझे इन सहायक कंटेनरों में से बहुत से तरीके बनाने हैं




मेरे पास अनुकूलक के बिना समाधान है एक्सएमएल-तत्वों में परिवर्तित क्षणिक नक्शा और विसे के विपरीत:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "SchemaBasedProperties")
public class SchemaBasedProperties
{
  @XmlTransient
  Map<String, Map<String, String>> properties;

  @XmlAnyElement(lax = true)
  List<Object> xmlmap;

  public Map<String, Map<String, String>> getProperties()
  {
    if (properties == null)
      properties = new LinkedHashMap<String, Map<String, String>>(); // I want same order

    return properties;
  }

  boolean beforeMarshal(Marshaller m)
  {
    try
    {
      if (properties != null && !properties.isEmpty())
      {
        if (xmlmap == null)
          xmlmap = new ArrayList<Object>();
        else
          xmlmap.clear();

        javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory.newInstance();
        javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();
        org.w3c.dom.Document doc = db.newDocument();
        org.w3c.dom.Element element;

        Map<String, String> attrs;

        for (Map.Entry<String, Map<String, String>> it: properties.entrySet())
        {
          element = doc.createElement(it.getKey());
          attrs = it.getValue();

          if (attrs != null)
            for (Map.Entry<String, String> at: attrs.entrySet())
              element.setAttribute(at.getKey(), at.getValue());

          xmlmap.add(element);
        }
      }
      else
        xmlmap = null;
    }
    catch (Exception e)
    {
      e.printStackTrace();
      return false;
    }

    return true;
  }

  void afterUnmarshal(Unmarshaller u, Object p)
  {
    org.w3c.dom.Node node;
    org.w3c.dom.NamedNodeMap nodeMap;

    String name;
    Map<String, String> attrs;

    getProperties().clear();

    if (xmlmap != null)
      for (Object xmlNode: xmlmap)
        if (xmlNode instanceof org.w3c.dom.Node)
        {
          node = (org.w3c.dom.Node) xmlNode;
          nodeMap = node.getAttributes();

          name = node.getLocalName();
          attrs = new HashMap<String, String>();

          for (int i = 0, l = nodeMap.getLength(); i < l; i++)
          {
            node = nodeMap.item(i);
            attrs.put(node.getNodeName(), node.getNodeValue());
          }

          getProperties().put(name, attrs);
        }

    xmlmap = null;
  }

  public static void main(String[] args)
    throws Exception
  {
    SchemaBasedProperties props = new SchemaBasedProperties();
    Map<String, String> attrs;

    attrs = new HashMap<String, String>();
    attrs.put("ResId", "A_LABEL");
    props.getProperties().put("LABEL", attrs);

    attrs = new HashMap<String, String>();
    attrs.put("ResId", "A_TOOLTIP");
    props.getProperties().put("TOOLTIP", attrs);

    attrs = new HashMap<String, String>();
    attrs.put("Value", "hide");
    props.getProperties().put("DISPLAYHINT", attrs);

    javax.xml.bind.JAXBContext jc = javax.xml.bind.JAXBContext.newInstance(SchemaBasedProperties.class);

    Marshaller marshaller = jc.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    marshaller.marshal(props, new java.io.File("test.xml"));

    Unmarshaller unmarshaller = jc.createUnmarshaller();
    props = (SchemaBasedProperties) unmarshaller.unmarshal(new java.io.File("test.xml"));

    System.out.println(props.getProperties());
  }
}

मेरे आउटपुट के रूप में देखा गया:

<SchemaBasedProperties>
    <LABEL ResId="A_LABEL"/>
    <TOOLTIP ResId="A_TOOLTIP"/>
    <DISPLAYHINT Value="hide"/>
</SchemaBasedProperties>

{LABEL={ResId=A_LABEL}, TOOLTIP={ResId=A_TOOLTIP}, DISPLAYHINT={Value=hide}}

आप तत्व का नाम / मान जोड़ी का उपयोग कर सकते हैं। मुझे विशेषताओं की आवश्यकता है ... मज़े!




मैंने कुछ भी नहीं देखा जो वास्तव में इस बहुत अच्छी तरह से उत्तर दिया। मैंने कुछ पाया जो बहुत अच्छी तरह से यहाँ काम किया:

गतिशील तत्व नामों को वापस करने के लिए JAXB XMLAnyElement शैली का उपयोग करें

मैंने इसे हलमाप के पेड़ों का समर्थन करने के लिए थोड़ा बदल दिया। आप अन्य संग्रह जोड़ सकते हैं

public class MapAdapter extends XmlAdapter<MapWrapper, Map<String, Object>> {
    @Override
    public MapWrapper marshal(Map<String, Object> m) throws Exception {
        MapWrapper wrapper = new MapWrapper();
        List elements = new ArrayList();
        for (Map.Entry<String, Object> property : m.entrySet()) {

            if (property.getValue() instanceof Map)
                elements.add(new JAXBElement<MapWrapper>(new QName(getCleanLabel(property.getKey())),
                        MapWrapper.class, marshal((Map) property.getValue())));
            else
                elements.add(new JAXBElement<String>(new QName(getCleanLabel(property.getKey())),
                        String.class, property.getValue().toString()));
        }
        wrapper.elements = elements;
        return wrapper;
    }

    @Override
    public Map<String, Object> unmarshal(MapWrapper v) throws Exception {
        // TODO
        throw new OperationNotSupportedException();
    }

    // Return a XML-safe attribute.  Might want to add camel case support
    private String getCleanLabel(String attributeLabel) {
        attributeLabel = attributeLabel.replaceAll("[()]", "").replaceAll("[^\\w\\s]", "_").replaceAll(" ", "_");
        return attributeLabel;
    }
}
class MapWrapper {
    @XmlAnyElement
    List elements;
}

फिर इसे लागू करने के लिए:

static class myxml {
    String name = "Full Name";
    String address = "1234 Main St";
    // I assign values to the map elsewhere, but it's just a simple
    // hashmap with a hashmap child as an example.
    @XmlJavaTypeAdapter(MapAdapter.class)
    public Map<String, Object> childMap;
}

यह एक सरल मार्शलर के माध्यम से भोजन को ऐसे उत्पादन देता है जो इस तरह दिखता है:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myxml>
    <name>Full Name</name>
    <address>1234 Main St</address>
    <childMap>
        <key2>value2</key2>
        <key1>value1</key1>
        <childTree>
            <childkey1>childvalue1</childkey1>
        </childTree>
    </childMap>
</myxml>



मुझे सबसे आसान समाधान मिला

@XmlElement(name="attribute")
    public String[] getAttributes(){
        return attributes.keySet().toArray(new String[1]);
    }
}

अब यह आपके जैसे एक्सएमएल आउटपुट को उत्पन्न करेगा:

<attribute>key1<attribute>
...
<attribute>keyN<attribute>