0

これは私の以前の投稿のフォローアップです: String to XmlNode Delphi (or how to add an x​​ml fragment to TXMLDocument) 新しい質問を始めるのが適切だと思われました...

基本的に、整形式の xml スニペットを既存の xmldocument に追加しています。前の解決策で提案されたコードは、[poPreserveWhiteSpace] を TXMLDocument.ParseOptions に追加するまではうまく機能していました。

[poPreserveWhiteSpace] を削除すると、すべて正常に動作しますが、空白は保持されません。実際には、終了タグを新しい行に配置します。

Target TXMLDocument のコード スニペットを次に示します。

  StoredXMLObj := TXMLDocument.Create(self);
  StoredXMLObj.Options := [doNodeAutoCreate, doNodeAutoIndent];
  StoredXMLObj.ParseOptions := StoredXMLObj.ParseOptions + [poPreserveWhiteSpace];
  StoredXMLObj.XML.Assign(StoredXML);  //StoredXML is a TStringList with a complete XML Document
  StoredXMLObj.Active := TRUE;

上記の Options と ParseOptions のさまざまな組み合わせを試しましたが、[poPreserveWhiteSpace] を削除することによってのみコードを機能させることができます。

例外をトリガーするコードは、次の 2 行目です。

tmpNode := storedXMLObj.DocumentElement.ChildNodes[i]; // <Class> node
tmpNode.ChildNodes.Nodes[1].ChildNodes.Nodes[0].ChildNodes.Add(LoadXMLData(MissingElements[j]).DocumentElement); //TMPNode is an IXMLNode and MissingElements is a TStringList

xml スニペットを追加する前に、LoadXMLData(..) の戻り値への参照を作成し、それらの ParseOptions を一致するように設定しようとしましたが、うまくいきませんでした。

何かご意見は?

編集:問題を示すために自己完結型のサンプル コードを追加します。タイトルを明確化。 ここにいくつかの単純化されたコードがあります。[poPreserveWhitespace] を含む行をコメントアウトしない限り、例外が発生することに注意してください。**編集 2: Remy の提案に従って、空白を保持するようにコードを微調整します。FormatXMLData を呼び出すときにまだ問題があります。

procedure TForm2.BitBtn2Click(Sender: TObject);
var
  FragmentXMLObj : TXMLDocument;
  StoredXMLObj : TXMLDocument;
  FragNode : IXMLNode;  //THIS SHOULD BE IXMLNODE, RIGHT?
  XMLStarting, XMLFragment, XMLMerged : TStringList;
  i : integer;
begin
//StringLists to hold xml data
  XMLStarting := TStringList.Create;  //COMPLETE XML
  XMLFragment := TStringList.Create;  //XML FRAGMENT TO INSERT INTO COMPLETE XML
  XMLMerged := TStringList.Create;    //MERGE OF THE ABOVE TWO.

//STARTING XML
  XMLStarting.Add('<?xml version="1.0" encoding="UTF-16" standalone="no"?>');
  XMLStarting.Add('<Programs>');
  XMLStarting.Add(' <Program_Group Batch_No="{12345678-1234-1234-1234-123456789ABC}" Description="FOO_824_1">');
  XMLStarting.Add('     <Program Name="PROG_1">');
  XMLStarting.Add('         <Class Name="CLASS_1">');
  XMLStarting.Add('             <Property Name="DB" RttiType="tkString">      </Property>');
  XMLStarting.Add('             <Property Name="SystemDate" RttiType="tkClass" ClassType="TXSDATE">12/30/1899</Property>');
  XMLStarting.Add('         </Class>');
  XMLStarting.Add('     </Program>');
  XMLStarting.Add(' </Program_Group>');
  XMLStarting.Add('</Programs>');

//XML DOCUMENT OBJECT
  StoredXMLObj := TXMLDocument.create(self);
  //PROBLEM LINE START
  StoredXMLObj.ParseOptions := StoredXMLObj.ParseOptions + [poPreserveWhiteSpace];
  //PROBLEM LINE END
  StoredXMLObj.Options := [doNodeAutoCreate, doNodeAutoIndent];
  StoredXMLObj.XML.Text := XMLStarting.Text;
  StoredXMLObj.Active := TRUE;

//XML FRAGMENT WITH SPACES
  XMLFragment.Add('<ParentNode>');
  XMLFragment.Add('<Property Name="VRSN" RttiType="tkString">    </Property>');
  XMLFragment.Add('<Property Name="ShowMetaData" RttiType="tkBoolean">     </Property>');
  XMLFragment.Add('</ParentNode>');

//--OLD CODE THAT RAISES EXCEPTION--
//INSERTING XML FRAGMENT INTO STARTING XML
//  FragNode := storedXMLObj.DocumentElement.ChildNodes[0];
//  FragNode.ChildNodes.Nodes[0].ChildNodes.Nodes[0].ChildNodes.Add(LoadXMLData(XMLFragment.Text).DocumentElement.ChildNodes.Nodes[0]);
//  FragNode.ChildNodes.Nodes[0].ChildNodes.Nodes[0].ChildNodes.Add(LoadXMLData(XMLFragment.Text).DocumentElement.ChildNodes.Nodes[1]);
//--OLD CODE THAT RAISES EXCEPTION--

  FragNode := storedXMLObj.DocumentElement.ChildNodes[1];
  FragmentXMLObj := TXMLDocument.Create(self);
  FragmentXMLObj.ParseOptions := FragmentXMLObj.ParseOptions + [poPreserveWhiteSpace];
  FragmentXMLObj.Options := [doNodeAutoCreate, doNodeAutoIndent];
  FragmentXMLObj.LoadFromXML(XMLFragment.Text);

  //FragNode.ChildNodes.Nodes[1].ChildNodes.Nodes[1].ChildNodes.Add(FragmentXMLObj.DocumentElement);  //this also pulls in the parent tags, which I don't want.
  for i := 0 to FragmentXMLObj.DocumentElement.ChildNodes.Count-1 do  //easier to just pull in all the nodes (including whitespace, then formatxml to cleanup).
    FragNode.ChildNodes.Nodes[1].ChildNodes.Nodes[1].ChildNodes.Add(FragmentXMLObj.DocumentElement.ChildNodes.Nodes[i]);
  FragmentXMLObj.Free;

  XMLMerged.Text := StoredXMLObj.XML.Text;
  XMLMerged.Text := FormatXMLData(XMLMerged.Text);  //UGH... FormatXMLData WIPES OUT WHITESPACE PROPERTY VALUES!!  Doesn't seem to have any settings either...
  XMLMerged.SaveToFile('c:\merged.xml');

  XMLStarting.Free;
  XMLFragment.Free;
  XMLMerged.Free;
  StoredXMLObj.Free;
end;

マージされた XML ファイルの結果... フォーマット中に空白のプロパティ値が消去されました (そして、データをフォーマットする必要がありますが、それは本当に醜いです)。

<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<Programs>
  <Program_Group Batch_No="{12345678-1234-1234-1234-123456789ABC}" Description="FOO_824_1">
    <Program Name="PROG_1">
      <Class Name="CLASS_1">
        <Property Name="DB" RttiType="tkString"/>
        <Property Name="SystemDate" RttiType="tkClass" ClassType="TXSDATE">12/30/1899</Property>
        <Property Name="VRSN" RttiType="tkString"/>
        <Property Name="ShowMetaData" RttiType="tkBoolean"/>
      </Class>
    </Program>
  </Program_Group>
</Programs>
4

1 に答える 1

2

LoadXMLData()は、入力文字列が整形式の XML ドキュメントであると想定しています。以前の質問に対して私が提供した解決策は、それ自体がスタンドアロンのドキュメントとして機能する個々の XML 要素を指定していたため、うまくいきました。しかし、PCDATA 要素自体は整形式の XML ドキュメントではありません。偽の要素でラップしてみてください。

tmpDoc := LoadXMLData('<Doc>' + MissingElements[j] + '</Doc>').DocumentElement;
for I := 0 to tmpDoc.ChildNodes.Count-1 do
  tmpNode.ChildNodes[1].ChildNodes[0].ChildNodes.Add(tmpDoc.ChildNodes[I]);

更新: ChildNodes.

あなたが示したXMLを考えると:

XMLStarting.Add('<?xml version="1.0" encoding="UTF-16" standalone="no"?>');
XMLStarting.Add('<Programs>');
XMLStarting.Add(' <Program_Group Batch_No="{12345678-1234-1234-1234-123456789ABC}" Description="FOO_824_1">');
XMLStarting.Add('     <Program Name="PROG_1">');
XMLStarting.Add('         <Class Name="CLASS_1">');
XMLStarting.Add('             <Property Name="DB" RttiType="tkString">      </Property>');
XMLStarting.Add('             <Property Name="SystemDate" RttiType="tkClass" ClassType="TXSDATE">12/30/1899</Property>');
XMLStarting.Add('         </Class>');
XMLStarting.Add('     </Program>');
XMLStarting.Add(' </Program_Group>');
XMLStarting.Add('</Programs>');

そして、失敗しているあなたが示したコードを考えると:

FragNode := storedXMLObj.DocumentElement.ChildNodes[0];
FragNode.ChildNodes.Nodes[0].ChildNodes.Nodes[0].ChildNodes.Add(LoadXMLData(XMLFragment.Text).DocumentElement.ChildNodes.Nodes[0]);

次のことが当てはまります。

  1. storedXMLObj.DocumentElementノードを指し <Programs>ます。
  2. そのChildNodes[0]ノードはノードの間の空白<Programs><Program_Group><Program_Group>を参照しますが、代わりにノードを参照することを期待しています。
  3. したがって、FragNode は子を持たないテキストのみのノードであるFragNode.ChildNodes.Nodes[0]ため、失敗します。

それはあなた自身で確認できます。 FragNode.NodeNameis '#text'FragNode.NodeTypeis ntTextFragNode.NodeValueis #$A' 'FragNode.HasChildNodesis False 、およびFragNode.IsTextElementis True です。

つまり、上記の XML には次の構造があります。

ntElement 'Programs'
|
|_ ntText #$A' '
|
|_ ntElement 'Program_Group'
   |
   |_ ntText #$A'     '
   |
   |_ ntElement 'Program'
   |  |
   |  |_ ntText #$A'         '
   |  |
   |  |_ ntElement 'Class'
   |  |  |
   |  |  |_ ntText #$A'             '
   |  |  |
   |  |  |_ nElement 'Property'
   |  |  |  |
   |  |  |  |_ ntText '      '
   |  |  |
   |  |  |_ ntText #$A'             '
   |  |  |
   |  |  |_ ntElement 'Property'
   |  |  |  |
   |  |  |  |_ ntText '12/30/1899'
   |  |  |
   |  |  |_ ntText #$A'         '
   |  |
   |  |_ ntText #$A'     '
   |
   |_ ntText #$A' '

うまくいけば、それが少し明確になります。

したがって、あなたがやろうとしていることを達成するには、次のようなものがもっと必要になります:

FragNode := storedXMLObj.DocumentElement.ChildNodes[1];
FragNode.ChildNodes.Nodes[1].ChildNodes.Nodes[1].ChildNodes.Add(LoadXMLData(XMLFragment.Text).DocumentElement);
FragNode.ChildNodes.Nodes[1].ChildNodes.Nodes[1].ChildNodes.Add(LoadXMLData(XMLFragment.Text).DocumentElement);

フラグメント内の空白を保持したい場合は、フラグを設定できないためLoadXMLData()、代わりに TXMLDocument を直接使用する必要があります。LoadXMLData()poPreserveWhiteSpace

FragmentXMLObj := TXMLDocument.Create(self);
FragmentXMLObj.ParseOptions := FragmentXMLObj.ParseOptions + [poPreserveWhiteSpace];
FragmentXMLObj.Options := [doNodeAutoCreate, doNodeAutoIndent];
FragmentXMLObj.LoadFromXML(XMLFragment.Text);
FragNode.ChildNodes.Nodes[1].ChildNodes.Nodes[1].ChildNodes.Add(FragmentXMLObj.DocumentElement);
FragmentXMLObj.Free;

インデックスに関する問題を回避するには、代わりに XPath クエリを使用することをお勧めします。これにより、フラグメントを挿入するノードをChildNodesDOM で検索できるようになります。<Class>

いずれにせよ、これでは見栄えの良い XML が生成されないことがすぐにわかります。空白を存在させたいだけで、実際には元の空白をそのまま保持する必要がない場合は、poPreserveWhiteSpaceフラグを無効にしてからFormatXMLData()、最終的なドキュメントを保存するときに使用することをお勧めします。

XMLMerged.Text := FormatXMLData(StoredXMLObj.XML.Text);
于 2013-05-31T21:57:05.467 に答える