Вложенные объекты через атрибут

#java #xml #jaxb

#java #xml #jaxb

Вопрос:

Я пытаюсь проанализировать XML-файл с вложенными объектами, на которые ссылается уникальный атрибут.

 <items>
   <item id="firstItem" />
   <item id="secondItem">
      <dependencies>
         <dependency id="firstItem" />
      </dependencies>
   </item>
</items>
  

В настоящее время я использую обычный способ и создал новый класс для каждого тега.

 @XmlRootElement(name = "items")
public class Items {
   private List<Item> items = new ArrayList();

   @XmlElement(name = "item")
   public List<Item> getItems() {
      return items;
   }
   /* setter */
}

public class Item {
   private string id;
   private Dependencies dependencies = new Dependencies();

   @XmlAttribute(name = "id")
   public String getId() {
      return id;
   }
   /* setter */

   @XmlElement(name = "dependencies")
   public Dependencies getDependencies() {
      return dependencies;
   }
   /* setter */
}

public class Dependencies {
   private List<Dependency> dependencies = new ArrayList();

   @XmlElement(name = "dependency") 
   public List<Dependency> getDependencies() {
      return dependencies;
   }
   /* setter */
}

public class Dependency {
   private String id;

   @XmlAttribute(name = "id")
   public String getId() {
      return id;
   }
   /* setter */
}
  

и прочитайте файл, используя javax.xml.bind.Разархивировщик

 Items items = new items();
JAXBContext context = JAXBContext.newInstance(Items.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
items = (Items) unmarshaller.unmarshal(/* My InputStream */);
  

Предпочтительным результатом был бы единый класс, который может содержать список самого себя.

 public class Item {
   private List<Item> dependencies = new ArrayList();
}
  

Возможно ли это с помощью инструментов xml?

Ответ №1:

Вам нужно написать пользовательский адаптер, но даже при этом у вас нет доступа к родительскому списку элементов. Таким образом, вы можете десериализовать идентификаторы в пользовательском адаптере и связать Item объекты после десериализации. Пример реализации:

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

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
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 java.io.File;
import java.util.ArrayList;
import java.util.List;

public class JaxbApp {

    public static void main(String[] args) throws Exception {
        File xmlFile = new File("./resource/test.xml").getAbsoluteFile();

        JAXBContext context = JAXBContext.newInstance(Items.class);

        Unmarshaller unmarshaller = context.createUnmarshaller();
        Items root = (Items) unmarshaller.unmarshal(xmlFile);

        System.out.println(root);
    }
}

class ItemXmlAdapter extends XmlAdapter<Object, Item> {

    @Override
    public Item unmarshal(Object v) {
        Element element = (Element) v;
        Node dependencies = element.getFirstChild();
        List<String> ids = new ArrayList<>();
        if (dependencies != null) {
            NodeList childNodes = dependencies.getChildNodes();
            for (int i = 0; i < childNodes.getLength(); i  ) {
                Node item = childNodes.item(i);
                NamedNodeMap attributes = item.getAttributes();
                if (attributes != null) {
                    Node id = attributes.getNamedItem("id");
                    if (id != null) {
                        ids.add(id.getNodeValue());
                    }
                }
            }
        }

        Item item = new Item();
        item.setId(element.getAttribute("id"));
        item.setDependencyIds(ids);

        return item;
    }

    @Override
    public Object marshal(Item v) throws Exception {
        return null; // Implement if needed
    }
}

@XmlRootElement(name = "items")
class Items {

    private List<Item> items = new ArrayList<>();

    @XmlElement(name = "item")
    public List<Item> getItems() {
        return items;
    }
}

@XmlJavaTypeAdapter(ItemXmlAdapter.class)
class Item {

    private String id;
    private List<String> dependencyIds;
    private List<Item> dependencies;

    // getters, setters
}
  

Для ваших XML отпечатков:

 Items{items=[Item{id='firstItem', dependencyIds=[]}, Item{id='secondItem', dependencyIds=[firstItem]}]}