2013-05-17 33 views
4

我想將XML文檔解析爲Java中的DOM樹,使樹中的某些對象(例如org.w3c.dom.Nodeorg.w3c.dom.Element的實例)可以向下轉換爲類我已經創建了,同時儘量減少了需要(重新)實現的與XML相關的代碼量。作爲(非常簡單的)例如,如果我有一個像XML元素:使用Java中的自定義對象實現將XML解析到DOM樹中

<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)是必要的?

+0

您是否掌握了XML的外觀? –

+0

是的,我完全控制了XML及其模式。 – TechnocratiK

回答

1

我不確定你的要求是什麼,但假設你在控制XML的外觀,我會用的是XStream。它將允許你完全跳過所有的DOM操作。

現在從their 2 minute tutorial,它可能看起來不像它是爲這個用例而構建的,但它實際上是。首先創建您的java類,確保它們以您希望的方式生成XML,然後使用它將您已經存在的XML作爲XStream對象讀回到程序中。這是一個非常愉快的圖書館使用。

+0

我需要對我的對象(即其成員)所做的更改立即反映在DOM中。看來XStream從DOM實例化了一組單獨的對象。 – TechnocratiK

+0

爲什麼你需要一個DOM? –

+0

主要使用XPath來導航XML層次結構。 – TechnocratiK

0

備註:我是EclipseLink JAXB (MOXy)的領導和JAXB (JSR-222)專家組的成員。

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

注:使用莫西作爲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

要指定莫西爲您的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(輸入。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 
0

我經歷過的正是這種在過去的10年裏,建設XML DOM的用於化學,圖形,數學等。我自己的解決方案是使用一個DOM,其中的元素可以被分類(我使用xom.nu,但也有其他的)。 w3c dom不允許子類化(IIRC),所以你將不得不建立一個委託模型。 (我在很多年以前就嘗試過並拒絕它,但是軟件工具和庫使這一切變得更加容易(例如,IDE將生成委託方法)

如果您正在做很多事情,尤其是如果您要創建很多定製的方法,那麼我會推薦滾動你自己的系統。這一努力將在你的方法(dotProduct),而不是XML。

這裏,例如,is my class for a 3D point

public class CMLPoint3 extends AbstractPoint3 

(延伸基地class 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作爲主引擎)。