2015-09-05 6 views
4

У меня есть XML-вход, который по существу является древовидной структурой. Цель состоит в том, чтобы (un) перевести код в классы Java.JAXB unmarshalling tree structure

<config> 

<key-value-pair> 
    <key>Key1</key> 
    <value>Value1</value> 
</key-value-pair> 

<key-value-pair> 
    <key>Key2</key> 
    <value> 
     <key-value-pair> 
     <key>Subkey2</key> 
     <value>Value999</value> 
     </key-value-pair> 
    </value> 
</key-value-pair> 

</config> 

XML, содержит типичный пар ключ/значение. И каждое значение может содержать либо другую пару «ключ/значение», либо пару «ключ/значение» или просто одно значение строки.

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
static class KeyValuePair { 

    @XmlElement(name="key") 
    private String key; 

    @XmlElement(name="value") 
    private String value; // here I don't know how to reflect 
          // the choice of String or another 
          // list of KeyValuePair objects 
    @XmlElement(name="value") 
    private List<KeyValuePair> valuePairs; 

    // getters/setters here 
} 

Тогда я просто еще одна обертка Class

@XmlRootElement(name="config") 
@XmlAccessorType(XmlAccessType.FIELD) 
static class Structure { 

    @XmlElement(name="key-value-pair") 
    private List<KeyValuePair> keyValuePair; 

    // getters/setters 
} 

И здесь есть логика, я пытаюсь использовать для (не) маршалинге.

Unmarshaller jaxbUnmarshaller = jc.createUnmarshaller(); 
StringReader reader = new StringReader(input); 
Structure struct = (Structure) jaxbUnmarshaller.unmarshal(reader); 
// struct doesn't get the data correctly ... 

Это то, что я получил до сих пор. Это не работает так, как у меня есть, но я надеюсь, что мне ясно, как должна выглядеть конечная цель.

Я хочу взять XML в начале и поставить экземпляр класса Structure.

+0

Добро пожаловать на переполнение стека! Это хороший, хорошо написанный вопрос, но не включайте фразовые фразы типа «Спасибо!». в вашем вопросе. Подумайте об этом сайте, как ближе к Википедии, чем о форуме. – durron597

+0

Не знаю, как выглядит JAXB, но я думаю, вам нужно сделать '' * mixed *. К сожалению, это позволит одновременно использовать как текст, так и элемент, и вам придется проверить это в Java-коде. См. [Здесь] (http://stackoverflow.com/questions/381782/xml-schema-element-that-can-contain-elements-or-text) для некоторого обсуждения того, как будет выглядеть XML-схема. – Andreas

+0

Я ценю ваших парней. Я больше ищут полное рабочее решение. Будет ли редактировать этот пост, когда я его найду. – Albrecht

ответ

0

Возможно, вы уже подумали об этом ... Если вы начнете с моделирования на уровне схемы, как это;

<?xml version="1.0" encoding="UTF-8"?> 
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> 
<xs:complexType name="key-value-pair"> 
    <xs:sequence> 
    <xs:element name="key" type="xs:string"/> 
    <xs:element name="value"> 
     <xs:complexType mixed="true"> 
      <xs:choice> 
       <xs:element name="key-value-pair" type="key-value-pair" minOccurs="0"/> 
      </xs:choice> 
     </xs:complexType> 
    </xs:element> 
    </xs:sequence> 
    </xs:complexType> 
    <xs:element name="config"> 
    <xs:complexType> 
     <xs:sequence> 
      <xs:element name="key-value-pair" type="key-value-pair" minOccurs="1" maxOccurs="unbounded"/> 
     </xs:sequence> 
    </xs:complexType> 
    </xs:element> 
</xs:schema> 

, а затем использовать XJC Ant Task или call it programmatically или независимо от того, чтобы генерировать классы JAXB вы в конечном итоге с

package uk.co.his.test.model; 

import java.io.Serializable; 
import java.util.ArrayList; 
import java.util.List; 
import javax.xml.bind.JAXBElement; 
import javax.xml.bind.annotation.XmlAccessType; 
import javax.xml.bind.annotation.XmlAccessorType; 
import javax.xml.bind.annotation.XmlElement; 
import javax.xml.bind.annotation.XmlElementRef; 
import javax.xml.bind.annotation.XmlMixed; 
import javax.xml.bind.annotation.XmlType; 


/** 
* <p>Java class for key-value-pair complex type. 
* 
* <p>The following schema fragment specifies the expected content contained within this class. 
* 
* <pre> 
* &lt;complexType name="key-value-pair"&gt; 
* &lt;complexContent&gt; 
*  &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt; 
*  &lt;sequence&gt; 
*   &lt;element name="key" type="{http://www.w3.org/2001/XMLSchema}string"/&gt; 
*   &lt;element name="value"&gt; 
*   &lt;complexType&gt; 
*    &lt;complexContent&gt; 
*    &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt; 
*     &lt;choice&gt; 
*     &lt;element name="key-value-pair" type="{}key-value-pair" minOccurs="0"/&gt; 
*     &lt;/choice&gt; 
*    &lt;/restriction&gt; 
*    &lt;/complexContent&gt; 
*   &lt;/complexType&gt; 
*   &lt;/element&gt; 
*  &lt;/sequence&gt; 
*  &lt;/restriction&gt; 
* &lt;/complexContent&gt; 
* &lt;/complexType&gt; 
* </pre> 
* 
* 
*/ 
@XmlAccessorType(XmlAccessType.FIELD) 
@XmlType(name = "key-value-pair", propOrder = { 
    "key", 
    "value" 
}) 
public class KeyValuePair { 

    @XmlElement(required = true) 
    protected String key; 
    @XmlElement(required = true) 
    protected KeyValuePair.Value value; 

    /** 
    * Gets the value of the key property. 
    * 
    * @return 
    *  possible object is 
    *  {@link String } 
    *  
    */ 
    public String getKey() { 
     return key; 
    } 

    /** 
    * Sets the value of the key property. 
    * 
    * @param value 
    *  allowed object is 
    *  {@link String } 
    *  
    */ 
    public void setKey(String value) { 
     this.key = value; 
    } 

    /** 
    * Gets the value of the value property. 
    * 
    * @return 
    *  possible object is 
    *  {@link KeyValuePair.Value } 
    *  
    */ 
    public KeyValuePair.Value getValue() { 
     return value; 
    } 

    /** 
    * Sets the value of the value property. 
    * 
    * @param value 
    *  allowed object is 
    *  {@link KeyValuePair.Value } 
    *  
    */ 
    public void setValue(KeyValuePair.Value value) { 
     this.value = value; 
    } 


    /** 
    * <p>Java class for anonymous complex type. 
    * 
    * <p>The following schema fragment specifies the expected content contained within this class. 
    * 
    * <pre> 
    * &lt;complexType&gt; 
    * &lt;complexContent&gt; 
    *  &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt; 
    *  &lt;choice&gt; 
    *   &lt;element name="key-value-pair" type="{}key-value-pair" minOccurs="0"/&gt; 
    *  &lt;/choice&gt; 
    *  &lt;/restriction&gt; 
    * &lt;/complexContent&gt; 
    * &lt;/complexType&gt; 
    * </pre> 
    * 
    * 
    */ 
    @XmlAccessorType(XmlAccessType.FIELD) 
    @XmlType(name = "", propOrder = { 
     "content" 
    }) 
    public static class Value { 
     @XmlElementRef(name = "key-value-pair", type = JAXBElement.class, required = false) 
     @XmlMixed 
     protected List<Serializable> content; 

     /** 
     * Gets the value of the content property. 
     * 
     * <p> 
     * This accessor method returns a reference to the live list, 
     * not a snapshot. Therefore any modification you make to the 
     * returned list will be present inside the JAXB object. 
     * This is why there is not a <CODE>set</CODE> method for the content property. 
     * 
     * <p> 
     * For example, to add a new item, do as follows: 
     * <pre> 
     * getContent().add(newItem); 
     * </pre> 
     * 
     * 
     * <p> 
     * Objects of the following type(s) are allowed in the list 
     * {@link String } 
     * {@link JAXBElement }{@code <}{@link KeyValuePair }{@code >} 
     * 
     * 
     */ 
     public List<Serializable> getContent() { 
      if (content == null) { 
       content = new ArrayList<Serializable>(); 
      } 
      return this.content; 
     } 

    } 

} 

Плюс ObjectFactory и класс Config (который, по существу, ваш класс Structure). ObjectFactory дает нам немного «сахара», чтобы помочь с ужасным танцем JAXBElement.

Поскольку Андреас сказал, что все равно разрешит String и вложенные KeyValuePair (ы). Затем вам придется обернуть класс Config с помощью проверки подлинности, чтобы проверить, что этого не происходит; например;

package uk.co.his.test; 

import java.io.Serializable; 
import java.util.List; 

import javax.xml.bind.JAXBElement; 
import javax.xml.bind.JAXBException; 

import uk.co.his.test.model.Config; 
import uk.co.his.test.model.KeyValuePair; 

public class Validate { 


    public void validate(Config c) throws JAXBException 
    { 
     for(KeyValuePair kvp: c.getKeyValuePair()) 
     { 
      validate(kvp); 
     } 
    } 

    public void validate(KeyValuePair kv) throws JAXBException 
    { 
     List<Serializable> mixed = kv.getValue().getContent(); 
     boolean nonWhitespaceStringFound = false; 
     boolean kvpFound = false; 
     for(Serializable c: mixed) 
     { 
      if(c instanceof String) 
      { 
       String s = (String) c; 
       if(s.trim().length()>0) { 
        nonWhitespaceStringFound = true; 
       } 
      } 
      else 
      { 
       @SuppressWarnings("unchecked") 
       JAXBElement<KeyValuePair> t = (JAXBElement<KeyValuePair>) c; 
       KeyValuePair child = t.getValue(); 
       kvpFound = true; 
       validate(child); 
      } 
      if(kvpFound && nonWhitespaceStringFound) { 
       throw new JAXBException("KeyValuePair "+kv.getKey()+" value element contained String data and nested KeyValuePair(s)"); 
      } 
     } 
    } 
} 

Чтобы создать конфигурацию, вам необходимо выполнить танцы JAXBElement;

private static final File Test1Out = new File("files/test1.xml"); 
@Test 
public void test1() throws JAXBException 
{ 
    ObjectFactory of = new ObjectFactory(); 
    Config c = new Config(); 
    c.getKeyValuePair().add(createKVPair(of, 2, 2, "a", "one")); 
    c.getKeyValuePair().add(createKVPair(of, 2, 1, "b", "two")); 
    c.getKeyValuePair().add(createKVPair(of, 0, 0, "c", "three")); 
    JAXBContext jbc = JAXBContext.newInstance("uk.co.his.test.model"); 
    Marshaller m = jbc.createMarshaller(); 
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); 
    m.marshal(c, Test1Out); 

    Unmarshaller u = jbc.createUnmarshaller(); 
    Config c2 = (Config) u.unmarshal(Test1Out); 
    Assert.assertTrue("Round trip produces different things", c2.getKeyValuePair().size() ==3); 
} 

private KeyValuePair createKVPair(ObjectFactory of, int depth, int length, String initialKey, String value) { 
    KeyValuePair kv = new KeyValuePair(); 
    kv.setKey(initialKey); 
    Value v = new Value(); 
    kv.setValue(v); 
    if(depth==0) 
    { 
     v.getContent().add(value); 
    } 
    else 
    { 
     int newdepth = --depth; 
     for(int i = 0; i < length; i++) 
     { 
      v.getContent().add(of.createKeyValuePairValueKeyValuePair(createKVPair(of, newdepth, length, initialKey+depth, value+i))); 
     } 
    } 
    return kv; 
} 

Ну, это начало ...