2009-03-15 37 views
13

Tôi có hai tệp XML có cấu trúc tương tự mà tôi muốn hợp nhất thành một tệp. Hiện tại tôi đang sử dụng EL4J XML Merge mà tôi đã xem qua trong hướng dẫn này. Tuy nhiên, nó không hợp nhất như tôi mong đợi cho các trường hợp vấn đề chính là nó không hợp nhất cả hai tập tin thành một phần tử, một trong đó có 1, 2, 3 và 4. Thay vào đó nó chỉ loại bỏ 1 và 2 hoặc 3 và 4 tùy thuộc vào tệp nào được hợp nhất trước. Vì vậy, tôi sẽ biết ơn bất cứ ai có kinh nghiệm với XML Merge nếu họ có thể cho tôi biết những gì tôi có thể làm sai hoặc cách khác không ai biết về một API XML tốt cho Java có khả năng hợp nhất các tệp như tôi yêu cầu?Hợp nhất hai tệp XML trong Java

Nhiều Nhờ sự giúp đỡ của bạn trong Advance

Edit:

thực sự có thể làm với một số gợi ý tốt về làm điều này để bổ sung thêm một tiền thưởng. Tôi đã thử đề xuất jdigital nhưng vẫn gặp vấn đề với hợp nhất XML.

Dưới đây là mẫu của loại cấu trúc của tệp XML mà tôi đang cố hợp nhất.

<run xmloutputversion="1.02"> 
    <info type="a" /> 
    <debugging level="0" /> 
    <host starttime="1237144741" endtime="1237144751"> 
     <status state="up" reason="somereason"/> 
     <something avalue="test" test="alpha" /> 
     <target> 
      <system name="computer" /> 
     </target> 
     <results> 
      <result id="1"> 
       <state value="test" /> 
       <service value="gamma" /> 
      </result> 
      <result id="2"> 
       <state value="test4" /> 
       <service value="gamma4" /> 
      </result> 
     </results> 
     <times something="0" /> 
    </host> 
    <runstats> 
     <finished time="1237144751" timestr="Sun Mar 15 19:19:11 2009"/> 
     <result total="0" /> 
    </runstats> 
</run> 

<run xmloutputversion="1.02"> 
    <info type="b" /> 
    <debugging level="0" /> 
    <host starttime="1237144741" endtime="1237144751"> 
     <status state="down" reason="somereason"/> 
     <something avalue="test" test="alpha" /> 
     <target> 
      <system name="computer" /> 
     </target> 
     <results> 
      <result id="3"> 
       <state value="testagain" /> 
       <service value="gamma2" /> 
      </result> 
      <result id="4"> 
       <state value="testagain4" /> 
       <service value="gamma4" /> 
      </result> 
     </results> 
     <times something="0" /> 
    </host> 
    <runstats> 
     <finished time="1237144751" timestr="Sun Mar 15 19:19:11 2009"/> 
     <result total="0" /> 
    </runstats> 
</run> 

sản lượng dự kiến ​​

<run xmloutputversion="1.02"> 
    <info type="a" /> 
    <debugging level="0" /> 
    <host starttime="1237144741" endtime="1237144751"> 
     <status state="down" reason="somereason"/> 
     <status state="up" reason="somereason"/> 
     <something avalue="test" test="alpha" /> 
     <target> 
      <system name="computer" /> 
     </target> 
     <results> 
      <result id="1"> 
       <state value="test" /> 
       <service value="gamma" /> 
      </result> 
      <result id="2"> 
       <state value="test4" /> 
       <service value="gamma4" /> 
      </result> 
      <result id="3"> 
       <state value="testagain" /> 
       <service value="gamma2" /> 
      </result> 
      <result id="4"> 
       <state value="testagain4" /> 
       <service value="gamma4" /> 
      </result> 
     </results> 
     <times something="0" /> 
    </host> 
    <runstats> 
     <finished time="1237144751" timestr="Sun Mar 15 19:19:11 2009"/> 
     <result total="0" /> 
    </runstats> 
</run> 
+0

Bạn có thể thêm các kết quả mong muốn? –

+0

Đã thêm kết quả mong đợi khi thêm kết quả vào nút kết quả là điều cricual nhất. –

Trả lời

0

Bạn có thể có thể viết một ứng dụng java mà deserilizes các tài liệu XML thành các đối tượng, sau đó "hợp nhất" các đối tượng cá nhân lập trình thành một bộ sưu tập. Sau đó bạn có thể tuần tự hóa đối tượng thu thập trở lại một tệp XML với mọi thứ "đã hợp nhất".

API JAXB có một số công cụ có thể chuyển đổi tài liệu/lược đồ XML thành các lớp java. Công cụ "xjc" có thể thực hiện được điều này, mặc dù tôi không thể nhớ nếu bạn có thể tạo các lớp trực tiếp từ tài liệu XML, hoặc nếu trước tiên bạn phải tạo một lược đồ. Có những công cụ ngoài đó có thể tạo ra một lược đồ từ một tài liệu XML.

Hy vọng điều này sẽ giúp ... không chắc chắn nếu đây là những gì bạn đang tìm kiếm.

+0

Cảm ơn bạn đã trả lời nó không thực sự những gì tôi đã có trong tâm trí nhưng sẽ giữ như là một lựa chọn nếu không có ai đi lên với một giải pháp. –

1

Tôi đã xem liên kết được tham chiếu; thật kỳ lạ khi XMLMerge sẽ không hoạt động như mong đợi. Ví dụ của bạn có vẻ đơn giản. Bạn đã đọc phần có tiêu đề Using XPath declarations with XmlMerge? Sử dụng ví dụ này, hãy thử thiết lập XPath cho kết quả và thiết lập nó để hợp nhất. Nếu tôi đọc tài liệu chính xác, nó sẽ trông giống như sau:

XPath.resultsNode=results 
action.resultsNode=MERGE 
+0

Tôi đã thử điều này nhưng nó vẫn không hoạt động đúng không may, tôi sẽ có một cái nhìn xung quanh xem nếu tôi có thể tìm thấy một số tài liệu tốt hơn cho nó. –

2

Nó có thể giúp bạn rõ ràng về kết quả mà bạn muốn đạt được. Đây có phải là những gì bạn đang yêu cầu không?

Doc A:

<root> 
    <a/> 
    <b> 
    <c/> 
    </b> 
</root> 

Doc B:

<root> 
    <d/> 
</root> 

Merged Kết quả:

<root> 
    <a/> 
    <b> 
    <c/> 
    </b> 
    <d/> 
</root> 

Bạn có lo lắng về việc mở rộng quy mô cho các tài liệu lớn?

Cách dễ nhất để thực hiện điều này trong Java là sử dụng trình phân tích cú pháp XML trực tuyến (google cho 'java StAX'). Nếu bạn sử dụng javax.xml.stream library bạn sẽ thấy rằng XMLEventWriter có một phương thức tiện lợi XMLEventWriter # add (XMLEvent). Tất cả những gì bạn phải làm là lặp qua các phần tử mức cao nhất trong mỗi tài liệu và thêm chúng vào nhà văn của bạn bằng cách sử dụng phương pháp này để tạo ra kết quả đã hợp nhất của bạn. Phần funky duy nhất là thực hiện logic người đọc chỉ xem xét (chỉ gọi 'thêm') trên các nút cấp cao nhất.

Gần đây, tôi đã triển khai phương pháp này nếu bạn cần gợi ý.

-6

Bạn đã xem xét việc không làm phiền việc phân tích cú pháp XML "đúng" và chỉ xử lý các tệp dưới dạng chuỗi dài lớn và sử dụng những thứ cũ nhàm chán như bản đồ băm và cụm từ thông dụng ...? Đây có thể là một trong những trường hợp mà các từ viết tắt lạ mắt với X trong chúng chỉ làm cho công việc trở nên khó hiểu hơn là cần thiết.

Rõ ràng điều này phụ thuộc một chút vào lượng dữ liệu bạn thực sự cần phân tích khi thực hiện hợp nhất. Nhưng bởi âm thanh của mọi thứ, câu trả lời cho điều đó không nhiều.

+0

bạn có thể garantee rằng chuỗi thẳng sẽ tái tạo XML đúng không?Bao nhiêu xác nhận và thử nghiệm là bạn sẵn sàng để đưa vào giải pháp đó so với "rắc rối" của việc sử dụng công cụ X mà sẽ mất rằng phụ trách? – Newtopian

+0

Nếu các tập tin mẫu được đưa ra là đại diện, và yêu cầu được nêu rõ, thì tôi nghĩ, có, tôi có thể. Nếu có một số phần ẩn đối với vấn đề (các tệp ở các định dạng khác nhau, rất nhiều yêu cầu xác thực), thì thực tế nhất có thể là phân tích cú pháp "đúng". –

0

Ngoài việc sử dụng Stax (có ý nghĩa), nó có thể dễ dàng hơn với StaxMate (http://staxmate.codehaus.org/Tutorial). Chỉ cần tạo 2 SMInputCursors và con trỏ nếu cần. Và sau đó sắp xếp hợp nhất điển hình với 2 con trỏ. Tương tự như duyệt các tài liệu DOM theo cách đệ quy.

+0

URL đã cho (http://staxmate.codehaus.org) dường như yêu cầu xác thực. Bạn có thể xác minh và cập nhật liên kết, vui lòng không. – rexford

+0

Phải, Codehaus đã không hoạt động. Dự án đã chuyển đến https://github.com/FasterXML/StaxMate. Cảm ơn bạn đã chỉ ra. – StaxMan

11

Không rất thanh lịch, nhưng bạn có thể làm điều này với các phân tích cú pháp DOM và XPath:

public class MergeXmlDemo { 

    public static void main(String[] args) throws Exception { 
    // proper error/exception handling omitted for brevity 
    File file1 = new File("merge1.xml"); 
    File file2 = new File("merge2.xml"); 
    Document doc = merge("/run/host/results", file1, file2); 
    print(doc); 
    } 

    private static Document merge(String expression, 
     File... files) throws Exception { 
    XPathFactory xPathFactory = XPathFactory.newInstance(); 
    XPath xpath = xPathFactory.newXPath(); 
    XPathExpression compiledExpression = xpath 
     .compile(expression); 
    return merge(compiledExpression, files); 
    } 

    private static Document merge(XPathExpression expression, 
     File... files) throws Exception { 
    DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory 
     .newInstance(); 
    docBuilderFactory 
     .setIgnoringElementContentWhitespace(true); 
    DocumentBuilder docBuilder = docBuilderFactory 
     .newDocumentBuilder(); 
    Document base = docBuilder.parse(files[0]); 

    Node results = (Node) expression.evaluate(base, 
     XPathConstants.NODE); 
    if (results == null) { 
     throw new IOException(files[0] 
      + ": expression does not evaluate to node"); 
    } 

    for (int i = 1; i < files.length; i++) { 
     Document merge = docBuilder.parse(files[i]); 
     Node nextResults = (Node) expression.evaluate(merge, 
      XPathConstants.NODE); 
     while (nextResults.hasChildNodes()) { 
     Node kid = nextResults.getFirstChild(); 
     nextResults.removeChild(kid); 
     kid = base.importNode(kid, true); 
     results.appendChild(kid); 
     } 
    } 

    return base; 
    } 

    private static void print(Document doc) throws Exception { 
    TransformerFactory transformerFactory = TransformerFactory 
     .newInstance(); 
    Transformer transformer = transformerFactory 
     .newTransformer(); 
    DOMSource source = new DOMSource(doc); 
    Result result = new StreamResult(System.out); 
    transformer.transform(source, result); 
    } 

} 

này giả định rằng bạn có thể nắm giữ ít nhất hai trong số những tài liệu trong RAM cùng một lúc.

+0

Điều này có vẻ đáng khích lệ mặc dù sẽ tốt hơn để được dynmaic hơn. Bạn có bất kỳ tài nguyên tốt nào để đọc thêm về trình phân tích cú pháp DOM và XPath hay không. –

+0

Có một hướng dẫn khá hay về devWorks: http://www.ibm.com/developerworks/library/x-javaxpathapi.html – McDowell

+1

+1: Bạn là anh hùng của tôi! :) – carlspring

0

Vì vậy, bạn chỉ quan tâm đến việc hợp nhất các yếu tố 'kết quả'? Mọi thứ khác đều bị bỏ qua? Thực tế là input0 có loại thông tin < = "a" /> và input1 có loại thông tin < = "b" /> và kết quả mong đợi có loại thông tin < = "a" /> dường như đề xuất điều này.

Nếu bạn không lo lắng về việc chia tỷ lệ và bạn muốn giải quyết vấn đề này một cách nhanh chóng thì tôi sẽ đề xuất viết một mã bit cụ thể cho vấn đề sử dụng một thư viện đơn giản như JDOM để xem xét đầu vào và ghi kết quả đầu ra.

Cố gắng viết một công cụ chung "đủ thông minh" để xử lý tất cả các trường hợp hợp nhất có thể sẽ tốn thời gian - bạn phải phơi bày khả năng cấu hình để xác định quy tắc hợp nhất. Nếu bạn biết chính xác dữ liệu của bạn sẽ trông như thế nào và bạn biết chính xác cách hợp nhất cần được thực thi thì tôi sẽ tưởng tượng thuật toán của bạn sẽ đi từng đầu vào XML và ghi vào một đầu ra XML đơn.

+0

Hơi khó để làm rõ bằng cách sử dụng hai tệp XML tôi có thể cần đăng một vài ví dụ quan trọng là một số nhóm như nút và đích sẽ hợp nhất hoặc thêm các phần tử mới một cách thích hợp. Nhưng những thứ khác như số liệu thống kê chạy có thể được để lại dưới dạng một nhóm duy nhất. –

0

Bạn có thể thử Dom4J cung cấp phương tiện rất tốt để trích xuất thông tin bằng truy vấn XPath và cũng cho phép bạn viết XML rất dễ dàng. Bạn chỉ cần chơi xung quanh API một lúc để thực hiện công việc của mình

3

Nhờ mọi người cho đề xuất của họ, không có phương pháp nào được đề xuất hóa ra là phù hợp vào cuối, vì tôi cần có quy tắc trong đó các nút khác nhau của cấu trúc được mô phỏng.

Vì vậy, những gì tôi đã làm là lấy DTD liên quan đến các tệp XML mà tôi đã hợp nhất và từ đó tạo ra một số lớp phản ánh cấu trúc. Từ này tôi đã sử dụng XStream để unserialize các tập tin XML trở lại vào các lớp học. Bằng cách này tôi chú thích các lớp của tôi làm cho nó một quá trình sử dụng một sự kết hợp của các quy tắc được gán với chú thích và một số phản ánh để hợp nhất các đối tượng như trái ngược với việc kết hợp cấu trúc XML thực tế.

Nếu có ai quan tâm đến mã trong trường hợp này hợp nhất các tệp XML Nmap, hãy xem http://fluxnetworks.co.uk/NmapXMLMerge.tar.gz các mã không hoàn hảo và tôi sẽ thừa nhận không linh hoạt, nhưng nó chắc chắn hoạt động. Tôi đang lập kế hoạch để reimplement hệ thống với nó phân tích cú pháp DTD tự động khi tôi có một số thời gian rảnh.

6

Tôi sử dụng XSLT để hợp nhất các tệp XML. Nó cho phép tôi điều chỉnh các hoạt động hợp nhất để chỉ slam nội dung với nhau hoặc để hợp nhất ở một cấp độ cụ thể. Đó là một công việc nhỏ hơn một chút (và cú pháp XSLT là loại đặc biệt) nhưng siêu linh hoạt. Một số điều bạn cần ở đây

a) Bao gồm một tập tin bổ sung b) Sao chép tập tin gốc 1: 1 c) Thiết kế điểm hợp nhất của bạn có hoặc không có sự trùng lặp tránh

a) Ban đầu, tôi có

<xsl:param name="mDocName">yoursecondfile.xml</xsl:param> 
<xsl:variable name="mDoc" select="document($mDocName)" /> 

này cho phép để trỏ đến file thứ hai sử dụng $ mDoc

b) các lệnh để sao chép một cây nguồn 1: 1 là 2 mẫu:

<!-- Copy everything including attributes as default action --> 
<xsl:template match="*"> 
    <xsl:element name="{name()}"> 
     <xsl:apply-templates select="@*" /> 
     <xsl:apply-templates /> 
    </xsl:element> 
</xsl:template> 

<xsl:template match="@*"> 
    <xsl:attribute name="{name()}"><xsl:value-of select="." /></xsl:attribute> 
</xsl:template> 

Không có gì khác, bạn sẽ nhận được bản sao 1: 1 của tệp nguồn đầu tiên của bạn. Làm việc với bất kỳ loại XML nào. Phần hợp nhất là tệp cụ thể. Giả sử bạn có các phần tử sự kiện với thuộc tính ID sự kiện. Bạn không muốn ID trùng lặp. Mẫu sẽ trông giống như sau:

<xsl:template match="events"> 
    <xsl:variable name="allEvents" select="descendant::*" /> 
    <events> 
     <!-- copies all events from the first file --> 
     <xsl:apply-templates /> 
     <!-- Merge the new events in. You need to adjust the select clause --> 
     <xsl:for-each select="$mDoc/logbook/server/events/event"> 
      <xsl:variable name="curID" select="@id" /> 
      <xsl:if test="not ($allEvents[@id=$curID]/@id = $curID)"> 
       <xsl:element name="event"> 
        <xsl:apply-templates select="@*" /> 
        <xsl:apply-templates /> 
       </xsl:element> 
      </xsl:if> 
     </xsl:for-each> 
    </properties> 
</xsl:template> 

Tất nhiên bạn có thể so sánh những thứ khác như tên thẻ, v.v. Nếu bạn không có khóa để so sánh, cấu trúc trở nên dễ dàng hơn, ví dụ: cho nhật ký:

<xsl:template match="logs"> 
    <xsl:element name="logs"> 
      <xsl:apply-templates select="@*" /> 
      <xsl:apply-templates /> 
      <xsl:apply-templates select="$mDoc/logbook/server/logs/log" /> 
    </xsl:element> 

Để chạy XSLT trong Java sử dụng này:

Source xmlSource = new StreamSource(xmlFile); 
    Source xsltSource = new StreamSource(xsltFile); 
    Result xmlResult = new StreamResult(resultFile); 
    TransformerFactory transFact = TransformerFactory.newInstance(); 
    Transformer trans = transFact.newTransformer(xsltSource); 
    // Load Parameters if we have any 
    if (ParameterMap != null) { 
     for (Entry<String, String> curParam : ParameterMap.entrySet()) { 
      trans.setParameter(curParam.getKey(), curParam.getValue()); 
     } 
    } 
    trans.transform(xmlSource, xmlResult); 

hoặc bạn tải về Saxon SAX Parser và làm điều đó từ dòng lệnh (Linux shell chẳng hạn):

#!/bin/bash 
notify-send -t 500 -u low -i gtk-dialog-info "Transforming $1 with $2 into $3 ..." 
# That's actually the only relevant line below 
java -cp saxon9he.jar net.sf.saxon.Transform -t -s:$1 -xsl:$2 -o:$3 
notify-send -t 1000 -u low -i gtk-dialog-info "Extraction into $3 done!" 

YMMV

+0

Bạn sẽ triển khai mã này bằng cách nào? Tôi không biết nhiều về XSLT nhưng tôi không thấy cách thực hiện XSLT này. – cjbarth

+2

Nguồn xmlSource = new StreamSource (xmlFile); Nguồn xsltSource = new StreamSource (xsltFile); Kết quả xmlResult = new StreamResult (resultFile); TransformerFactory transFact = TransformerFactory.newInstance(); Biến đổi trans = transFact.newTransformer (xsltSource); // Nạp thông số nếu chúng ta có bất kỳ if (ParameterMap = null) { cho (Entry curParam: ParameterMap.entrySet()) { \t trans.setParameter (curParam.getKey(), curParam .getValue()); \t} } trans.transform (xmlSource, xmlResult); – stwissel

+0

+1 XSLT chắc chắn là cách để đi cho hoạt động hợp nhất XML – yegor256

2

Đây là hình thức của giao diện XML:

action.default=MERGE 

xpath.info=/run/info 
action.info=PRESERVE 

xpath.result=/run/host/results/result 
action.result=MERGE 
matcher.result=ID 

Bạn phải đặt trình phù hợp ID cho nút kết quả // và đặt hành động PRESERVE cho nút // thông tin. Ngoài ra hãy cẩn thận rằng việc sử dụng XML .properties XML Merge có phân biệt chữ hoa chữ thường - bạn phải sử dụng "xpath" chứ không phải "XPath" trong tệp .properties của bạn.

Đừng quên để xác định tham số -config như thế này:

java -cp lib\xmlmerge-full.jar; ch.elca.el4j.services.xmlmerge.tool.XmlMergeTool -config xmlmerge.properties example1.xml example2.xml 
Các vấn đề liên quan