[java] जेएक्सबी: मानचित्र को <key> value </ key> में मार्शल कैसे करें



Answers

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

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

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

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

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

public enum KeyType {
 KEY, KEY2;
}

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

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

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

<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

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

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

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

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

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

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

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




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

डायनामिक तत्व नामों को वापस करने के लिए जेएक्सबी एक्सएमएलएनीलेमेंट प्रकार का स्टाइल का उपयोग करें

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

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>



मेरे पास एडाप्टर के बिना समाधान है। क्षणिक मानचित्र xml-elements में परिवर्तित हो गया है और vise versa:

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

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




Links