2009-03-15 9 views
13

私は1つのファイルに結合したい同様の構造の2つのXMLファイルを持っています。 現在、私はこのチュートリアルで遭遇したEL4J XML Mergeを使用しています。 しかし、私は主な問題は、両方のファイルから1つの要素、別名1、2、3、4をマージしていないことが予想されるのでマージしません。 代わりに1と2または3のいずれかを破棄しますどのファイルが最初にマージされるかに応じて4つのファイルがあります。Javaの2つのXMLファイルを結合する

XML Mergeの経験がある人には、私が何が間違っているのかを教えてもらえればいいと思うか、誰かがXML用の良いAPIを知っていれば、ファイルをマージすることができます必要ですか?アドバンス

であなたの助けを

多くのおかげ

編集:

が本当にこれを行うにはいくつかの良い提案を行うことができるように恵みを追加しました。私はjdigitalの提案を試みたが、依然としてXMLマージに問題がある。

以下は、マージしようとしているXMLファイルの構造のサンプルです。

<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> 

の予想される出力

<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

あなたが希望する結果を追加してもらえますか? –

+0

期待される出力を追加しました。結果ノードへの結果の追加がもっともらしいものです。 –

答えて

0

あなたがオブジェクトにXML文書をdeserilizes Javaアプリケーションを書くことができるかもしれませんが、その後、プログラム的コレクションに個々のオブジェクトを「マージ」。その後、コレクションオブジェクトをすべて「マージされた」XMLファイルにシリアル化することができます。

JAXB APIには、XMLドキュメント/スキーマをJavaクラスに変換できるツールがいくつかあります。 XML文書から直接クラスを作成できるかどうか、またはスキーマを最初に生成する必要があるかどうかはわかりませんが、 "xjc"ツールでこれを行うことができます。そこには、XML文書からスキーマを生成できるツールがあります。

これが役に立ったら...あなたが探していたものかどうかは分かりません。

+0

あなたのおかげで本当に私が気にしていたことに答えることに感謝しますが、誰も別の解決策が出てこない場合、オプションとして保持します。 –

1

私は参照されたリンクを見ました。 XMLMergeが期待どおりに動作しないことは間違いです。あなたの例は簡単です。 Using XPath declarations with XmlMergeというセクションを読んだことがありますか?この例を使用して、結果のXPathを設定して、マージするように設定してみてください。あなたはあなたが達成することに興味を持っている結果について明示的であればそれが役立つかもしれない

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

私はこれを試しましたが、まだ残念ながらうまくいきませんでした。私はそれについてのよりよい文書を見つけることができるかどうか見ていきます。 –

2

:私が正しくドキュメントを読んでいるなら、それは次のようになります。あなたが求めているのはこれですか?

ドクA:

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

ドクB:

<root> 
    <d/> 
</root> 

マージ結果:

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

はあなたが大規模な文書のスケーリングを心配していますか?

これをJavaで実装する最も簡単な方法は、ストリーミングXMLパーサー(google for 'java StAX')を使用することです。 javax.xmlを使用する場合XMLEventWriterに便利なメソッドXMLEventWriter#add(XMLEvent)があることがわかります。あなたがしなければならないのは、各ドキュメントのトップレベル要素をループし、このメソッドを使ってマージ結果を生成することです。ファンキーな部分は、トップレベルのノードでのみ考慮する(「add」を呼び出すだけの)読者ロジックを実装することです。

ヒントが必要な場合は、最近このメソッドを実装しました。

-6

XMLを「適切に」解析し、ファイルを長い長い文字列として扱い、ハッシュマップや正規表現などの退屈な古いものを使用するだけで構いませんでしたか?これは、Xを使った派手な頭字語が、必要以上に仕事を控えめにするケースの1つになる可能性があります。

これは明らかに、マージの実行中に実際に解析する必要のあるデータ量に依存します。しかし、物事の音によって、その答えはあまりありません。

+0

ストレート文字列が適切なXMLを再生成することを保証できますか?どのような検証とテストが、そのソリューションを置くことを望んでいますか?それを担当するXツールを使用することの「厄介な問題」ですか? – Newtopian

+0

与えられたサンプルファイルが代表的なものであり、要件が記載されている場合、私はそう思います。問題に隠された部分(さまざまな形式のファイル、多くの検証が必要)がある場合、最も実用的なのは「適切に」解析することです。 –

0

Stax(それは意味をなさない)を使用することに加えて、おそらくStaxMate(http://staxmate.codehaus.org/Tutorial)を使用する方が簡単でしょう。 2つのSMInputCursorsと必要に応じて子カーソルを作成するだけです。そして、典型的なマージソートは2つのカーソルで行います。再帰的に降下する方法でDOM文書をトラバースするのと同様です。

+0

指定されたURL(http://staxmate.codehaus.org)は認証が必要なようです。リンクを確認して更新してください。 – rexford

+0

いいえ、Codehausは残念ながらシャットダウンしませんでした。プロジェクトはhttps://github.com/FasterXML/StaxMateに移動しました。指摘していただきありがとうございます。 – StaxMan

11

非常にエレガントではない、しかし、あなたは、DOMパーサーと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); 
    } 

} 

これは同時にRAM内のドキュメントの少なくとも二つを保持できることを前提としています。

+0

これはより多くのdynmaicであることがよりよいが、これは有望に見えます。 DOMパーサとXPathについて詳しく知りたい人はいらっしゃいますか? –

+0

devWorksについてのチュートリアルがあります。http://www.ibm.com/developerworks/library/x-javaxpathapi.html – McDowell

+1

+1:あなたは私のヒーローです! :) – carlspring

0

「結果」要素のマージのみに興味がありますか?それ以外は無視されますか? input0が<のinfo type = "a" />とinput1を持っているという事実は、<のinfo type = "b" />で、予期した結果は< info type = "a" /です。

スケーリングについて心配しておらず、この問題をすばやく解決したい場合は、JDOMのような単純なライブラリを使用して入力を検討し、出力結果を書き込む問題固有のコードを書くことをおすすめします。

可能なすべてのマージケースを処理するのに十分な「スマート」な汎用ツールを作成しようとするとかなり時間がかかります。マージルールを定義するには構成機能を公開する必要があります。あなたのデータがどのように見えるか正確に分かっていて、マージがどのように実行される必要があるかを正確に知っているなら、あなたのアルゴリズムが各XML入力を歩き、単一のXML出力に書き込むだろうと思います。

+0

2つのXMLファイルを使用して明確にするのは難しいですが、いくつかの例を投稿する必要があるかもしれません。ノードやターゲットなどのいくつかのグループは、新しい要素を適切にマージまたは追加するだけです。しかし、実行統計のような他のものは、1つのグループとして残すことができます。 –

0

Dom4Jを試すと、XPathクエリを使用して情報を抽出する非常に良い手段を提供し、XMLを非常に簡単に書くこともできます。あなたはちょうどあなたの仕事をするためにしばらくAPIで遊ぶ必要があります

3

残念なことに、残念なことに、提案された方法のどれもが最終的には適していないことが判明しました。構造の異なるノードが単なるものである。

私がしたことは、マージしていたXMLファイルに関するDTDを取って、その構造を反映するいくつかのクラスを作成することでした。 これから私はXStreamを使って、XMLファイルを直列化してクラスに戻しました。

このようにして、実際のXML構造をマージするのではなく、オブジェクトをマージするために、アノテーションで割り当てられたルールとリフレクションの組み合わせを使用するプロセスにするクラスに注釈を付けました。

Nmap XMLファイルをマージするコードに興味がある人はhttp://fluxnetworks.co.uk/NmapXMLMerge.tar.gzコードは完璧ではないと私は大胆な柔軟性は認めませんが、確かに動作します。私はいくつかの自由時間があるときにDTDを自動的に解析するシステムを再実装するつもりです。

6

私はXMLファイルをマージするためにXSLTを使用します。私は、マージ操作を調整して、コンテンツをまとめたり、特定のレベルでマージしたりすることができます。もう少し作業が必要ですが(XSLT構文は特殊ですが)、非常に柔軟です。あなたは追加のファイル Bを含めるa)はこちら

を必要とするいくつかのこと)1元のファイルをコピーします:1 c)はa)の初めに、私は

を持つ重複回避の有無にかかわらず

をお使いのマージポイントを設計します

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

これはソースツリーをコピーするための命令は1)の$ MDOC

Bを用いて第2のファイルを指すことができます:

:1 2つのテンプレートです
<!-- 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> 

最初のソースファイルの1:1コピーは他にありません。任意のタイプのXMLで動作します。マージパートはファイル固有です。イベントID属性を持つイベント要素があるとしましょう。重複したIDは必要ありません。あなたはまた、それがマージが起こるどのように深いあなた次第ですタグ名などのような他のものを比較することができます。もちろん、

<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> 

:テンプレートは次のようになります。比較するキーがない場合、構造は簡単になります。ログ用:

<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> 

はJavaでXSLTを実行するには、この使用:

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); 

をか、(Linuxシェル例)Saxon SAX Parserをダウンロードして、コマンドラインからの操作を行います。

#!/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

これをコードでどのように実装しますか?私はXSLTについてあまりよく知っていませんが、このXSLTをどのように実行するのかわかりません。 – cjbarth

+2

ソースxmlSource =新しいStreamSource(xmlFile); ソースxsltSource =新しいStreamSource(xsltFile); 結果xmlResult =新しいStreamResult(resultFile); TransformerFactory transFact = TransformerFactory.newInstance(); トランストランス= transFact.newTransformer(xsltSource); //負荷パラメータ(たParameterMap = nullで!)場合は、我々はすべての を持っている場合は、{(エントリ<文字列、文字列> curParam:ParameterMap.entrySet())のための { \t trans.setParameter(curParam.getKey()、curParam .getValue()); \t} } trans.transform(xmlSource、xmlResult); – stwissel

+0

+1 XSLTは間違いなくXMLマージ操作に行く方法です – yegor256

2

これは、XMLマージの使用方法です。

action.default=MERGE 

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

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

//結果ノードにIDマッチャーを設定し、//情報ノードにPRESERVEアクションを設定する必要があります。また、.propertiesのXML Mergeでは大文字と小文字が区別されますので、.propertiesでは "XPath"ではなく "xpath"を使用する必要があります。

はこのよう-configパラメータを定義することを忘れないでください:

java -cp lib\xmlmerge-full.jar; ch.elca.el4j.services.xmlmerge.tool.XmlMergeTool -config xmlmerge.properties example1.xml example2.xml 
関連する問題