簡體   English   中英

使用Java中的自定義對象實現將XML解析為DOM樹

[英]Parse XML into DOM tree with custom object implementations in Java

我想用Java將XML文檔解析為DOM樹,以便樹中的某些對象(例如org.w3c.dom.Nodeorg.w3c.dom.Element實例)可以向下轉換為我擁有的類的實例創建,同時最小化我需要(重新)實現的XML相關代碼的數量。 作為一個(非常簡單的)示例,如果我有一個XML元素,如:

<Vector size="5">
  1.0 -1.0 3.0 -2.73e2
</Vector>

我想自定義解析器為它實例化以下內容:

public class Vector extends /* some parser class */ {
  private double[] elements;

  /* constructors; etc.*/

  public double dotProduct(Vector v) {
    /* implementation */
  }
}

這樣我就可以將解析器創建的Vector實例傳遞給javax.xml.xpath對象的方法,並讓它們正常工作。 實現這一目標的最快方法是什么? 單獨使用Java SE還是第三方庫(例如Xerces)是否必要?

我不確定你的要求是什么,但假設你控制了XML的樣子,我會使用的是XStream 它將允許您完全跳過所有DOM操作。

他們的2分鍾教程開始 ,它可能看起來不像是為這個用例而構建的,但實際上它是。 首先創建java類,確保它們以您希望的方式生成XML,然后使用它將已存在的XML作為XStream對象讀回到程序中。 這是一個非常令人愉快的圖書館。

注意:我是EclipseLink JAXB(MOXy)的負責人,也是JAXB(JSR-222)專家組的成員。

JAXB中的Binder機制可能正是您所尋找的。 它不允許將DOM節點強制轉換為域對象,但它確實維護域對象與其對應的DOM節點之間的鏈接。

注意:當使用MOXy作為JAXB提供程序時,以下代碼運行干凈,但在使用我正在運行的JDK版本中包含的JAXB的impl時拋出了異常。

JAVA模型

我將在此示例中使用以下域模型。

顧客

import java.util.*;
import javax.xml.bind.annotation.*;

@XmlRootElement
public class Customer {

    private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();

    @XmlElementWrapper
    @XmlElement(name="phoneNumber")
    public List<PhoneNumber> getPhoneNumbers() {
        return phoneNumbers;
    }

}

電話號碼

import javax.xml.bind.annotation.*;

public class PhoneNumber {

    private String type;
    private String number;

    @XmlAttribute
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @XmlValue
    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

}

jaxb.properties

要將MOXy指定為JAXB提供程序,您需要在與域模型相同的包中包含一個名為jaxb.properties的文件,並帶有以下條目(請參閱: http//blog.bdoughan.com/2011/05/specifying-eclipselink- moxy-as-your.html

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

XML(input.xml)

<?xml version="1.0" encoding="UTF-8"?>
<customer>
    <phoneNumbers>
        <phoneNumber type="work">555-1111</phoneNumber>
        <phoneNumber type="home">555-2222</phoneNumber>
    </phoneNumbers>
</customer>

演示代碼

在下面的演示代碼中,我將執行以下操作:

  1. 使用XPath查找子元素,然后使用Binder查找相應的域對象。
  2. 更新域對象並使用Binder將更改應用於DOM。
  3. 更新DOM並使用Binder將更改應用於域對象。

演示

import javax.xml.bind.Binder;
import javax.xml.bind.JAXBContext;
import javax.xml.parsers.*;
import javax.xml.xpath.*;

import org.w3c.dom.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.parse("src/forum16599580/input.xml");

        XPathFactory xpf = XPathFactory.newInstance();
        XPath xpath = xpf.newXPath();

        JAXBContext jc = JAXBContext.newInstance(Customer.class);
        Binder<Node> binder = jc.createBinder();
        binder.unmarshal(document);

        // Use Node to Get Object
        Node phoneNumberElement = (Node) xpath.evaluate("/customer/phoneNumbers/phoneNumber[2]", document, XPathConstants.NODE);
        PhoneNumber phoneNumber = (PhoneNumber) binder.getJAXBNode(phoneNumberElement);

        // Modify Object to Update DOM
        phoneNumber.setNumber("555-2OBJ");
        binder.updateXML(phoneNumber);
        System.out.println(xpath.evaluate("/customer/phoneNumbers/phoneNumber[2]", document, XPathConstants.STRING));

        // Modify DOM to Update Object
        phoneNumberElement.setTextContent("555-2DOM");
        binder.updateJAXB(phoneNumberElement);
        System.out.println(phoneNumber.getNumber());
    }

}

產量

555-2OBJ
555-2DOM

在過去的10年里,我一直在為化學,圖形,數學等構建XML DOM。我自己的解決方案是使用DOM,其中元素可以被子類化(我使用xom.nu,但還有其他) 。 w3c dom不允許子類化(IIRC),因此您必須構建委托模型。 (我多年前嘗試過並拒絕它,但軟件工具和庫使這一切變得更加容易(例如,IDE將生成委托方法)。

如果你做了很多,特別是如果你要創建很多自定義方法,那么我建議你自己編譯系統。 努力將在您的方法(dotProduct),而不是XML。

例如,這是我的3D點類

public class CMLPoint3 extends AbstractPoint3 

(擴展了基類CMLElement,擴展了nu.xom.Element

元素的創建是一個工廠。 這是我的SVGDOM的一大塊:

public static SVGElement readAndCreateSVG(Element element) {
    SVGElement newElement = null;
    String tag = element.getLocalName();
    if (tag == null || tag.equals(S_EMPTY)) {
        throw new RuntimeException("no tag");
    } else if (tag.equals(SVGCircle.TAG)) {
        newElement = new SVGCircle();
    } else if (tag.equals(SVGClipPath.TAG)) {
        newElement = new SVGClipPath();
    } else if (tag.equals(SVGDefs.TAG)) {
        newElement = new SVGDefs();
    } else if (tag.equals(SVGDesc.TAG)) {
        newElement = new SVGDesc();
    } else if (tag.equals(SVGEllipse.TAG)) {
        newElement = new SVGEllipse();
    } else if (tag.equals(SVGG.TAG)) {

...
    } else {
            newElement = new SVGG();
            newElement.setClassName(tag);
            System.err.println("unsupported svg element: "+tag);
        }
        if (newElement != null) {
            newElement.copyAttributesFrom(element);
            createSubclassedChildren(element, newElement);
        }
        return newElement;

您可以看到用於復制和遞歸的工具。

您需要考慮的問題是:

  • 這與XSD有多緊密相關
  • 使用XSD數據類型
  • 我對輸入進行驗證
  • 我使用DOM作為主要數據結構(我這樣做)
  • 事情變化的頻率。

FWIW我已經對此進行了6次修改,並且正在考慮另一個(使用Scala作為主要引擎)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM