2013-05-31 5 views
0

Это является продолжением моей предыдущей публикации: String to XmlNode Delphi (or how to add an xml fragment to TXMLDocument) Это представляется целесообразным, чтобы начать новый вопрос ...Delphi Исключение при Сохраняя Whitespace в TXMLDocument

Я по существу добавление хорошо сформированные XML-фрагменты в существующий XmlDocument , Код, предложенный в предыдущем решении, отлично работал - до тех пор, пока я не добавил [poPreserveWhiteSpace] в TXMLDocument.ParseOptions.

Когда я удаляю [poPreserveWhiteSpace], все работает отлично, но пробелы не сохраняются. Он фактически помещает закрывающий тег в новую строку.

Вот фрагмент кода из целевого документа 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; 

Я пробовал разные комбинации вариантов и ParseOptions выше, но я могу только получить код для работы путем удаления [poPreserveWhiteSpace].

код, который вызывает исключение является второй строкой:

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 

Я попытался создать ссылку на возвращаемом значение LoadXMLData (..), и установке этих ParseOptions, чтобы соответствовать, прежде чем добавлять XML фрагмент, но не повезло и там.

Любые мысли?

Редактировать: добавление самодостаточного образца кода для демонстрации проблемы. Уточненное название. Вот несколько упрощенных кодов. Обратите внимание, что будет исключение, если вы не закомментируете строку, содержащую [poPreserveWhitespace]. ** Edit2: Тонкая настройка кода для сохранения пробелов в соответствии с предложением Реми. Все еще проблема при вызове 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 ... Пробелы значения свойства получили уничтожены при форматировании (и мне нужно форматировать данные, OTW это действительно некрасиво).

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

ответ

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

Update: Вы получаете «индекс вне границ» ошибки, потому что вы не принимаете пробельные DOM узлов во внимание при обращении к 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.ChildNodes.Nodes[0] не работает, потому что FragNode - это текстовый узел, у которого нет детей!

Вы можете подтвердить это сами. FragNode.NodeName is '#text', FragNode.NodeType is ntText, FragNode.NodeValue is #$A' ', FragNode.HasChildNodes False, и FragNode.IsTextElement is 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; 

Чтобы избежать каких-либо проблем с ChildNodes индексами, то лучше использовать запрос XPath вместо этого, так что вы можете позволить DOM ищет узел <Class>, в который вы хотите вставить фрагменты.

В любом случае, вы скоро обнаружите, что это не создает очень красивый XML. Если вы просто хотите, чтобы там было пустое пространство, но на самом деле вам не нужно сохранять исходные пробелы как есть, тогда вам лучше отключить флаг poPreserveWhiteSpace, а затем использовать FormatXMLData() при сохранении окончательного документа:

XMLMerged.Text := FormatXMLData(StoredXMLObj.XML.Text); 
+0

Элементы, которые я добавляю, являются точно такими же хорошо сформированными элементами, как раньше. Мне непонятно, почему добавление сохранения белого пространства изменило бы поведение TXMLDocument. – sse

+0

Я тестировал код, используя те же элементы, что и раньше, и они были добавлены отлично с сохранением пробелов и без них. –

+0

Как я уже сказал, когда я попытался добавить новые узлы с сохранением пробелов, включенными в пункте назначения, это сработало для меня. Так происходит что-то еще. –