?
Solved

XML problems

Posted on 2005-04-22
10
Medium Priority
?
1,521 Views
Last Modified: 2013-11-19
I open a xml file in my prog with a XMLDoc.

I search for nodes with:

SNode:= XMLDoc.DocumentElement.ChildNodes.FindNode('one');
SNode:= SNode.ChildNodes.FindNode('onein');
temp := SNode.ChildNodes['myvalue'].Text;

All work perfect but I need to read this three values and don't know how:

<day d="0" t="Tuesday" dt="Apr 19">

I try with
temp := SNode.ChildNodes['day d'].Text; but tell my that I can have " here !?!?!?

The second problem is how can I detect these two nodes?

<part p="d">
<part p="n">
SNode:= SNode.ChildNodes.FindNode('part'); don't work to can identify each node.

HEEEEEELP!
0
Comment
Question by:ginsonic
  • 5
  • 5
10 Comments
 
LVL 26

Expert Comment

by:Russell Libby
ID: 13843853
ginsonic,

When you say you are using XMLDoc, do you mean the MSXML library? Regardless, the problem has to do with the consfusion between node names and node attributes. In your example:

<day d="0" t="Tuesday" dt="Apr 19">

day is the node name, and the attribute map is

[d]="0"
[t]="Tuesday"
[dt]="Apr 19

Just like the FindNode on part is unable to differentiate between the 2 nodes named part. The diff between them is the attribute map. If you let me know which XML implementation you are using, I can provide code to demonstrate.

Regards,
Russell

0
 
LVL 9

Author Comment

by:ginsonic
ID: 13844666
I have next units in my uses:
xmldom, XMLIntf, msxmldom, XMLDoc
0
 
LVL 9

Author Comment

by:ginsonic
ID: 13844683
And maybe can help with ... , too:

<part p="d">
  <a>my1</a>
  <b>my2</b>
</part>
<part p="n">
  <a>my3</a>
  <b>my4</b>
</part>

P.S. Sorry for bad details. Is my first xml application.
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
LVL 26

Expert Comment

by:Russell Libby
ID: 13845179

No problem on the details, as I follow what you are after...
Unfortunately, I'm at a slight disadvantage as I am currently working in D5 (haven't taken the time to install D2005 yet). What you *should* find though is that the Node exposes an Attributes collection, which will contain a list of nodes (which are the attributes). There would be 1 attribute type node (using the above example) for each child node,

node name=part
attributes[0] name=p, value = d

node name=part
attributes[0] name=p, value = n

If you are only parsing simple XML data (like shown above), and don't wish to carry all the overhead of the MSXML dom parser, then I can provide source that I use for XML parsing; it allows for node finding WITH the ability to specify attributes to search on as well. Its all source code, no other dependancies, and is less than 1K lines of code.

If you wish to stick with the MSXML DOM though, then I will install D2005 so we are both on the same page (though it may take awhile <g>)

Russell

0
 
LVL 26

Accepted Solution

by:
Russell Libby earned 2000 total points
ID: 13845570
An example using the parser I mentioned:

const
  XML            =  '<part p="d">'+
                    '<a>my1</a>'+
                    '<b>my2</b>'+
                    '</part>'+
                    '<part p="n">'+
                    '<a>my3</a>'+
                    '<b>my4</b>'+
                    '</part>'+
                    '<day d="0" t="Tuesday" dt="Apr 19">';


var  XmlNode:       TXMLNode;
     XmlDoc:        TXMLDocument;
     dwIndex:       Integer;
begin

  // Create document parse
  XmlDoc:=TXMLDocument.Create;

  // Load XML string
  XmlDoc.LoadXML(XML);

  // Example of finding nodes (will find first match)
  XmlNode:=XmlDoc.FindNode('part');
  if Assigned(XmlNode) then
  begin
     ShowMessage(XmlNode.NodeName);
     // Example of child nodes
     for dwIndex:=0 to Pred(XmlNode.NodeCount) do
        ShowMessage(Format('Node=%s, Text=%s', [XmlNode[dwIndex].NodeName, XmlNode[dwIndex].NodeText]));
  end;

  // Example of finding a specific node using name and attr list
  XmlNode:=XmlDoc.FindNodeEx('part', 'p=n;');
  if Assigned(XmlNode) then
  begin
     ShowMessage(XmlNode.NodeName);
     for dwIndex:=0 to Pred(XmlNode.NodeCount) do
        ShowMessage(Format('Node=%s, Text=%s', [XmlNode[dwIndex].NodeName, XmlNode[dwIndex].NodeText]));
  end;

  // Find the day node and display the attributes for it
  XmlNode:=XmlDoc.FindNode('day');
  if Assigned(XmlNode) then
  begin
     ShowMessage(XmlNode.NodeName);
     for dwIndex:=0 to Pred(XmlNode.AttributeCount) do
        ShowMessage(Format('Attribute=%s, Value=%s', [XmlNode.AttributeNames[dwIndex], XmlNode.AttributeValues[XmlNode.AttributeNames[dwIndex]]]));
  end;

  // Free document
  XmlDoc.Free;

end;


And source for the parser:

unit XMLParser;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit           :  XMLPARSER
//   Author         :  rllibby
//   Date           :  011.20.2004
//   Description    :  Provides a basic XML DOM parser class that was written to
//                     replace MSXML for simple tasks.
//
//
//   Attribute Query:
//
//      Attribute queries are performed by building a semi-colon delimited string
//      of attr name=value pairs to match on. The name must be specified, but if the
//      value should match anything, then a simple =; can be used, or just the
//      attribute name can be specified. Some examples
//
//      "id=100;value=testing"     // Both name and values
//      "id;value"                 // Just attribute names
//      "id=;value="               // Same as above
//
////////////////////////////////////////////////////////////////////////////////
interface

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows,
  SysUtils,
  Classes;

////////////////////////////////////////////////////////////////////////////////
//   XML transformation strings
////////////////////////////////////////////////////////////////////////////////
const
  XML_ACTUAL:       Array [0..4] of String = ('<', '>', '&', '"', '''');
  XML_TRANSFORM:    Array [0..4] of String = ('&lt;', '&gt;', '&amp;', '&quot;', '&apos;');

////////////////////////////////////////////////////////////////////////////////
//   XML types
////////////////////////////////////////////////////////////////////////////////
type
  TXMLNodeType      =  (xntUndefined, xntPI, xntDecl, xntComment, xntElement);

////////////////////////////////////////////////////////////////////////////////
//   XML node class
////////////////////////////////////////////////////////////////////////////////
type
  TXMLNode          =  class(TObject)
  private
     // Private declarations
     FParent:       TXMLNode;
     FType:         TXMLNodeType;
     FAttributes:   TStringList;
     FNodes:        TList;
     FName:         String;
     FText:         String;
     procedure      ParsePair(NameValue: String; var Name, Value: String);
     function       ParseName(XML: PChar): PChar;
     function       ParseText(XML: PChar): PChar;
     function       ParseAttributes(XML: PChar): PChar;
     function       ParseEnclosedName(XML: PChar): PChar;
     function       ParseString(XML: PChar; var Value: String): PChar;
     function       ParseWhitespace(XML: PChar): PChar;
  protected
     // Protected declarations
     function       GetNodeText: String;
     procedure      SetNodeName(Value: String);
     procedure      SetNodeText(Value: String);
     function       GetAttributeNames(Index: Integer): String;
     function       GetAttributeValues(Index: String): String;
     function       GetAttributeCount: Integer;
     function       GetNodes(Index: Integer): TXMLNode;
     function       GetNodeCount: Integer;
     function       ParseNode(XML: PChar): PChar;
     function       AttributeMatches(Query: String): Boolean;
  public
     // Public declarations
     constructor    Create(ParentNode: TXMLNode);
     destructor     Destroy; override;
     function       HasAttribute(Name: String): Boolean;
     function       FindNodeEx(Name: String; AttributeQuery: String): TXMLNode;
     function       FindNode(Name: String): TXMLNode;
     procedure      Clear;
     property       AttributeCount: Integer read GetAttributeCount;
     property       AttributeNames[Index: Integer]: String read GetAttributeNames;
     property       AttributeValues[Index: String]: String read GetAttributeValues;
     property       NodeCount: Integer read GetNodeCount;
     property       Nodes[Index: Integer]: TXMLNode read GetNodes; default;
     property       NodeName: String read FName write SetNodeName;
     property       NodeParent: TXMLNode read FParent;
     property       NodeText: String read GetNodeText write SetNodeText;
     property       NodeType: TXMLNodeType read FType;
  end;

////////////////////////////////////////////////////////////////////////////////
//   XML document class
////////////////////////////////////////////////////////////////////////////////
type
  TXMLDocument      = class(TObject)
  private
     // Private declarations
     FNodes:        TList;
     FLoaded:       Boolean;
  protected
     // Protected declarations
     procedure      ParseNodes(XML: PChar);
     function       GetNodes(Index: Integer): TXMLNode;
     function       GetNodeCount: Integer;
  public
     // Public declarations
     constructor    Create;
     destructor     Destroy; override;
     function       FindNodeEx(Name: String; AttributeQuery: String): TXMLNode;
     function       FindNode(Name: String): TXMLNode;
     procedure      Clear;
     procedure      LoadFromStream(XmlStream: TStream);
     procedure      LoadFromFile(XmlFile: String);
     procedure      LoadXML(XmlString: String);
     property       Loaded: Boolean read FLoaded;
     property       NodeCount: Integer read GetNodeCount;
     property       Nodes[Index: Integer]: TXMLNode read GetNodes; default;
  end;

implementation

function TXMLNode.AttributeMatches(Query: String): Boolean;
var  szQuery:       String;
     szName:        String;
     szValue:       String;
     dwPos:         Integer;
begin

  // Set default result (optimistic)
  result:=True;

  // Make a local copy of query
  szQuery:=Query;

  // Seed first name/value pair
  dwPos:=Pos(';', szQuery);

  // Loop while true
  while ((dwPos > 0) and result) do
  begin
     // Parse into name / value pair
     ParsePair(Copy(szQuery, 1, Pred(dwPos)), szName, szValue);
     // Remove pair from query string
     Delete(szQuery, 1, dwPos);
     // Check name
     if HasAttribute(szName) then
     begin
        // Attribute name is found, check value
        if (Length(szValue) > 0) then result:=(CompareText(FAttributes.Values[szName], szValue) = 0);
     end
     else
        // Allow for blank attribute name
        result:=(Length(szName) = 0);
     // If still true then get next position
     if result then dwPos:=Pos(';', szQuery);
  end;

  // Parse out the last pair
  if ((Length(szQuery) > 0) and result) then
  begin
     // Parse into name / value pair
     ParsePair(szQuery, szName, szValue);
     // Check name
     if HasAttribute(szName) then
     begin
        // Attribute name is found, check value
        if (Length(szValue) > 0) then result:=(CompareText(FAttributes.Values[szName], szValue) = 0);
     end
     else
        // Allow for blank attribute name
        result:=(Length(szName) = 0);
  end;

end;

procedure TXMLNode.ParsePair(NameValue: String; var Name, Value: String);
var  dwEqual:       Integer;
begin

  // Get the sep position
  dwEqual:=Pos('=', NameValue);

  // Check for sep
  if (dwEqual > 0) then
  begin
     // Parse into name / value
     Name:=Copy(NameValue, 1, Pred(dwEqual));
     Value:=Copy(NameValue, Succ(dwEqual), MaxWord);
  end
  else
  begin
     // Parse into name only
     Name:=NameValue;
     Value:=EmptyStr;
  end;

end;

function TXMLNode.ParseName(XML: PChar): PChar;
var  lpszXML:       PChar;
begin

  // Get string pointer
  lpszXML:=XML;

  // Resource protection
  try
     // Check first byte for node type indicator
     if (lpszXML^ in ['!', '?']) then
        // Parse enclosed name
        lpszXML:=ParseEnclosedName(lpszXML)
     else
     begin
        // Parse the node name
        lpszXML:=ParseString(lpszXML, FName);
        // Set element type
        FType:=xntElement;
     end;
  finally
     // Return current string position
     result:=lpszXML;
  end;

end;

function TXMLNode.ParseEnclosedName(XML: PChar): PChar;
var  lpszXML:       PChar;
     dwCount:       Integer;
     dwTag:         Integer;
begin

  // Get string pointer
  lpszXML:=XML;

  // Resource protection
  try
     // Set counter and tag count
     dwTag:=0;
     dwCount:=0;
     // Walk the string
     while (lpszXML^ > #0) do
     begin
        // Check for close tag
        if (lpszXML^ = '>') then
        begin
           // Found the closing tag (and not in embedded tag)
           if (dwTag > 0) then
              // Decrement the tag count
              Dec(dwTag)
           else
              // Done processing
              break;
        end;
        // Check for open tag
        if (lpszXML^ = '<') then Inc(dwTag);
        // Push counter and string
        Inc(dwCount);
        Inc(lpszXML);
     end;
     // Copy the parsed string into the name field
     SetString(FName, XML, dwCount);
     // Check tag type
     if (Length(FName) > 2) then
     begin
        // Check for processing instruction
        if (FName[1] = '?') then
           // PI type
           FType:=xntPI
        else if (Pos('!--', FName) = 1) then
           // Comment type
           FType:=xntComment
        else
           // Declaration type
           FType:=xntDecl;
     end;
  finally
     // Return current string position
     result:=lpszXML;
  end;

end;

function TXMLNode.ParseString(XML: PChar; var Value: String): PChar;
var  lpszXML:       PChar;
     dwCount:       Integer;
begin

  // Get string pointer
  lpszXML:=XML;

  // Resource protection
  try
     // Set counter
     dwCount:=0;
     // Walk the string
     while not(lpszXML^ in [#0, #9, #10, #13, #32, #47, #60..#62]) do
     begin
        // Push counter and string
        Inc(dwCount);
        Inc(lpszXML);
     end;
     // Copy the parsed string
     SetString(Value, XML, dwCount);
  finally
     // Return current string position
     result:=lpszXML;
  end;

end;

function TXMLNode.ParseWhitespace(XML: PChar): PChar;
var  lpszXML:       PChar;
begin

  // Get string pointer
  lpszXML:=XML;

  // Resource protection
  try
     // Parse out white space
     while (lpszXML^ in [#9, #10, #13, #32]) do Inc(lpszXML);
  finally
     // Return current string position
     result:=lpszXML;
  end;

end;

function TXMLNode.ParseAttributes(XML: PChar): PChar;
var  lpszXML:       PChar;
     lpszValue:     PChar;
     dwCount:       Integer;
     szAttrName:    String;
     szAttrValue:   String;
begin

  // Get string pointer
  lpszXML:=XML;

  // Resource protection
  try
     // Skip white space
     lpszXML:=ParseWhitespace(lpszXML);
     // While string left to parse and not terminated
     while not(lpszXML^ in [#0, '/', '>']) do
     begin
        // Parse out attribute name
        lpszXML:=ParseString(lpszXML, szAttrName);
        // Skip to the quote
        while not(lpszXML^ in [#0, #34]) do Inc(lpszXML);
        // Check for termination
        if (lpszXML^ = #0) then break;
        // Push past the quote
        Inc(lpszXML);
        // Save off string pointer
        lpszValue:=lpszXML;
        // Set count
        dwCount:=0;
        // Push to the ending quote
        while not(lpszXML^ in [#0, #34]) do
        begin
           Inc(dwCount);
           Inc(lpszXML);
        end;
        // Copy the attribute value
        SetString(szAttrValue, lpszValue, dwCount);
        // Push past the quote
        if (lpszXML^ > #0) then Inc(lpszXML);
        // Check for duplicate attributes
        if (FAttributes.IndexOfName(szAttrName) < 0) then
           // Add the name / value pair
           FAttributes.Add(Format('%s=%s', [szAttrName, szAttrValue]))
        else
           // Update the attribute value
           FAttributes.Values[szAttrName]:=szAttrValue;
        // Skip white space
        lpszXML:=ParseWhitespace(lpszXML);
     end;
  finally
     // Return current string position
     result:=lpszXML;
  end;

end;

function TXMLNode.ParseText(XML: PChar): PChar;
var  lpszXML:       PChar;
     lpszScan:      PChar;
     dwCount:       Integer;
     szText:        String;
begin

  // Get string pointer
  lpszXML:=XML;

  // Resource protection
  try
     // Set scan string pointer
     lpszScan:=ParseWhitespace(lpszXML);
     // Check scan pointer (no text if next non-ws char is <)
     if (lpszScan^ = '<') then
        // Set string pointer
        lpszXML:=lpszScan
     else
     begin
        // Set count
        dwCount:=0;
        // Push to the ending quote
        while not(lpszXML^ in [#0, '<']) do
        begin
           Inc(dwCount);
           Inc(lpszXML);
        end;
        // Copy the text value
        SetString(szText, XML, dwCount);
        // Append the text value to current text
        FText:=FText+szText;
     end;
  finally
     // Return current string pos
     result:=lpszXML;
  end;

end;

function TXMLNode.ParseNode(XML: PChar): PChar;
var  xnItem:        TXMLNode;
     lpszXML:       PChar;
begin

  // Get string pointer
  lpszXML:=XML;

  // Resource protection
  try
     // Check string
     if (lpszXML^ = '<') then
     begin
        // Push past the start tag
        Inc(lpszXML);
        // Parse name
        lpszXML:=ParseName(lpszXML);
        // Skip white space
        lpszXML:=ParseWhitespace(lpszXML);
        // Check for attribute settings
        if not(lpszXML^ in ['/', '>']) then lpszXML:=ParseAttributes(lpszXML);
        // Check for closing tag
        if (lpszXML^ = '/') then
        begin
           // Push to the closing tag and we are done
           while not(lpszXML^ in [#0, '>']) do Inc(lpszXML);
        end
        // Parse the child nodes / inner text for the node
        else if (FType = xntElement) then
        begin
           // Parse while string left
           while (lpszXML^ > #0) do
           begin
              // Skip ending tag
              if (lpszXML^ = '>') then Inc(lpszXML);
              // Parse out text
              lpszXML:=ParseText(lpszXML);
              // Check for </
              if (lpszXML[0] = '<') and (lpszXML[1] = '/') then
              begin
                 // Push past the 2 bytes
                 Inc(lpszXML, 2);
                 // Skip white space
                 lpszXML:=ParseWhitespace(lpszXML);
                 // Parse the tag name
                 lpszXML:=ParseName(lpszXML);
                 // Push past the closing >
                 if (lpszXML^ = '>') then Inc(lpszXML);
                 // Done processing
                 break;
              end
              else
              begin
                 // Create inner node
                 xnItem:=TXMLNode.Create(Self);
                 // Add node to internal list
                 FNodes.Add(xnItem);
                 // Parse the node text
                 lpszXML:=xnItem.ParseNode(lpszXML);
              end;
           end;
        end;
     end
     else
        // Push next
        Inc(lpszXML);
  finally
     // Return current string pos
     result:=lpszXML;
  end;

end;

function TXMLNode.GetAttributeNames(Index: Integer): String;
begin

  // Return the attribute name
  result:=FAttributes.Names[Index];

end;

function TXMLNode.GetAttributeValues(Index: String): String;
begin

  // Return the attribute value
  result:=FAttributes.Values[Index];

end;

function TXMLNode.GetAttributeCount: Integer;
begin

  // Return attribute count
  result:=FAttributes.Count;

end;

function TXMLNode.GetNodes(Index: Integer): TXMLNode;
begin

  // Return child node
  result:=TXMLNode(FNodes[Index]);

end;

function TXMLNode.GetNodeCount: Integer;
begin

  // Return node count
  result:=FNodes.Count;

end;

function TXMLNode.GetNodeText: String;
var  dwIndex:       Integer;
begin

  // Get string
  result:=FText;

  // Check string
  if not(result = EmptyStr) and (Pos('&', result) > 0) then
  begin
     // Transform to actual chars
     for dwIndex:=0 to High(XML_ACTUAL) do result:=StringReplace(result, XML_TRANSFORM[dwIndex], XML_ACTUAL[dwIndex], [rfReplaceAll, rfIgnoreCase]);
  end;

end;

procedure TXMLNode.SetNodeName(Value: String);
var  dwIndex:       Integer;
begin

  // Make sure the name is valid
  if (Length(Value) > 0) then
  begin
     // Validate name string
     for dwIndex:=1 to Length(Value) do
     begin
        if (Value[dwIndex] in [#0, #9, #10, #13, #32, #47, #60..#62]) then exit;
     end;
     // Set new node name
     FName:=Value;
  end;

end;

procedure TXMLNode.SetNodeText(Value: String);
var  dwIndex:       Integer;
begin

  // Set string
  FText:=Value;

  // Check string
  if not(FText = EmptyStr) and (Pos('&', FText) > 0) then
  begin
     // Convert to transform string
     for dwIndex:=0 to High(XML_ACTUAL) do FText:=StringReplace(FText, XML_ACTUAL[dwIndex], XML_TRANSFORM[dwIndex], [rfReplaceAll, rfIgnoreCase]);
  end;

end;

function TXMLNode.HasAttribute(Name: String): Boolean;
begin

  // Check string list for name
  result:=(FAttributes.IndexOfName(Name) >= 0);

end;

function TXMLNode.FindNodeEx(Name: String; AttributeQuery: String): TXMLNode;
var  dwIndex:       Integer;
begin

  // Check attribute query string
  if (Length(AttributeQuery) = 0) then
     // No attribute names passed, so basically a find node call
     result:=FindNode(Name)
  else
  begin
     // Set default result
     result:=nil;
     // Walk the nodes
     for dwIndex:=0 to Pred(FNodes.Count) do
     begin
        // Check names
        if (CompareText(TXMLNode(FNodes[dwIndex]).NodeName, Name) = 0) then
        begin
           // Query the attributes to see if they match
           if TXMLNode(FNodes[dwIndex]).AttributeMatches(AttributeQuery) then
           begin
              // Found the node
              result:=TXMLNode(FNodes[dwIndex]);
              // Done
              break;
           end;
        end;
     end;
  end;

end;

function TXMLNode.FindNode(Name: String): TXMLNode;
var  dwIndex:       Integer;
begin

  // Set default result
  result:=nil;

  // Walk the nodes and
  for dwIndex:=0 to Pred(FNodes.Count) do
  begin
     // Check names
     if (CompareText(TXMLNode(FNodes[dwIndex]).NodeName, Name) = 0) then
     begin
        // Found the node
        result:=TXMLNode(FNodes[dwIndex]);
        // Done
        break;
     end;
  end;

end;

procedure TXMLNode.Clear;
var  dwIndex:       Integer;
begin

  // Resource protection
  try
     // Free all child nodes
     for dwIndex:=Pred(FNodes.Count) downto 0 do TXMLNode(FNodes[dwIndex]).Free;
  finally
     // Clear list
     FNodes.Clear;
     // Clear all attribute items
     FAttributes.Clear;
     // Clear the text
     FText:=EmptyStr;
  end;

end;

constructor TXMLNode.Create(ParentNode: TXMLNode);
begin

  // Perform inherited
  inherited Create;

  // Set working defaults
  FAttributes:=TStringList.Create;
  FNodes:=TList.Create;
  FParent:=ParentNode;
  FType:=xntUndefined;
  SetLength(FName, 0);
  SetLength(FText, 0);

end;

destructor TXMLNode.Destroy;
begin

  // Resource protection
  try
     // Clear lists
     Clear;
     // Free the lists
     FreeAndNil(FNodes);
     FreeAndNil(FAttributes);
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

//// TXMLDocument //////////////////////////////////////////////////////////////
procedure TXMLDocument.ParseNodes(XML: PChar);
var  lpszXML:       PChar;
     xnItem:        TXMLNode;
begin

  // Get string pointer
  lpszXML:=XML;

  // Resource protection
  try
     // Parse the string
     while (lpszXML^ > #0) do
     begin
        // Check for start of tag
        if (lpszXML^ = '<') then
        begin
           // Create node
           xnItem:=TXMLNode.Create(nil);
           // Add node to list
           FNodes.Add(xnItem);
           // Parse node
           lpszXML:=xnItem.ParseNode(lpszXML);
        end
        else
           // Push next
           Inc(lpszXML);
     end;
  finally
     // Set loaded state
     FLoaded:=True;
  end;

end;

function TXMLDocument.FindNodeEx(Name: String; AttributeQuery: String): TXMLNode;
var  dwIndex:       Integer;
begin

  // Check attribute query string
  if (Length(AttributeQuery) = 0) then
     // No attribute names passed, so basically a find node call
     result:=FindNode(Name)
  else
  begin
     // Set default result
     result:=nil;
     // Walk the nodes
     for dwIndex:=0 to Pred(FNodes.Count) do
     begin
        // Check names
        if (CompareText(TXMLNode(FNodes[dwIndex]).NodeName, Name) = 0) then
        begin
           // Query the attributes to see if they match
           if TXMLNode(FNodes[dwIndex]).AttributeMatches(AttributeQuery) then
           begin
              // Found the node
              result:=TXMLNode(FNodes[dwIndex]);
              // Done
              break;
           end;
        end;
     end;
  end;

end;

function TXMLDocument.FindNode(Name: String): TXMLNode;
var  dwIndex:       Integer;
begin

  // Set default result
  result:=nil;

  // Walk the nodes and
  for dwIndex:=0 to Pred(FNodes.Count) do
  begin
     // Check names
     if (CompareText(TXMLNode(FNodes[dwIndex]).NodeName, Name) = 0) then
     begin
        // Found the node
        result:=TXMLNode(FNodes[dwIndex]);
        // Done
        break;
     end;
  end;

end;

function TXMLDocument.GetNodes(Index: Integer): TXMLNode;
begin

  // Return child node
  result:=TXMLNode(FNodes[Index]);

end;

function TXMLDocument.GetNodeCount: Integer;
begin

  // Return node count
  result:=FNodes.Count;

end;

procedure TXMLDocument.Clear;
var  dwIndex:       Integer;
begin

  // Resource protection
  try
     // Free all child nodes
     for dwIndex:=Pred(FNodes.Count) downto 0 do TXMLNode(FNodes[dwIndex]).Free;
  finally
     // Clear list
     FNodes.Clear;
     // Set loaded state
     FLoaded:=False;
  end;

end;

procedure TXMLDocument.LoadFromStream(XmlStream: TStream);
var  lpszXML:    PChar;
begin

  // Clear current root node
  Clear;

  // Allocate memory for parsing
  lpszXML:=AllocMem(Succ(XmlStream.Size));

  // Resource protection
  try
     // Reset the stream
     XmlStream.Position:=0;
     // Copy the data from the stream
     XmlStream.Read(lpszXML^, XmlStream.Size);
     // Parse the XML data
     ParseNodes(lpszXML);
  finally
     // Free memory
     FreeMem(lpszXML);
  end;

end;

procedure TXMLDocument.LoadFromFile(XmlFile: String);
var  fsStream:      TFileStream;
begin

  // Create file stream and pass to load from stream
  fsStream:=TFileStream.Create(XmlFile, fmOpenRead or fmShareDenyNone);

  // Resource protection
  try
     // Load from stream
     LoadFromStream(fsStream);
  finally
     // Free the stream
     FreeAndNil(fsStream);
  end;

end;

procedure TXMLDocument.LoadXML(XmlString: String);
var  lpszXML:       PChar;
begin

  // Clear current root node
  Clear;

  // Allocate memory for parsing
  lpszXML:=AllocMem(Succ(Length(XmlString)));

  // Copy the passed string
  StrPCopy(lpszXML, XmlString);

  // Resource protection
  try
     // Parse the XML data
     ParseNodes(lpszXML);
  finally
     // Free memory
     FreeMem(lpszXML);
  end;

end;

constructor TXMLDocument.Create;
begin

  // Perform inherited
  inherited Create;

  // Set working defaults
  FLoaded:=False;
  FNodes:=TList.Create;

end;

destructor TXMLDocument.Destroy;
begin

  // Resource protection
  try
     // Clear nodes
     Clear;
     // Free the node list
     FreeAndNil(FNodes);
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

end.

0
 
LVL 9

Author Comment

by:ginsonic
ID: 13848794
Will be nice if is possible to keep to XMLDoc who come with Delphi. I don't wish to rewrite my app.
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 13849916
I can understand that.

But did you check the nodes you are using for an Attributes collection (or something similiar)? And when you do find it, you realize that you will need to manually walk each node in order to find node's with the same name (by doing a comparison on the attr name/values).

Russell
0
 
LVL 9

Author Comment

by:ginsonic
ID: 13850139
I figure out a way using you comment. Still digging. Hope to finish in next 2-3 hours.
Thanks!

P.S. Exist a way to locate a nod using attributes but with standard XMLDoc and not your unit? I ask because at this moment I use my Delphi TXMLDocument and renamed your with TrlbXMLDocument (same name).
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 13850287
Okay, this is based on code defs I found for the delphi XML wrapper around the MSXML dom interfaces. I can't test this until monday, but it should give you the idea of what you after. It does allow you to pass an attr query in the same fashion as the component listed above; based on the code you have of course

Russell



function FindNodeEx(Node: IXMLNode; Name: String; AttributeQuery: String): IXMLNode;
var  AttrNode:      IXMLNode;
     AttrName:      String;
     AttrText:      String;
     NodeName:      String;
     AttrList:      TStringList;
     dwIndex:       Integer;
     dwAttr:        Integer;

  procedure ParsePair(NameValue: String; var Name, Value: String);
  var  dwEqual:       Integer;
  begin
     // Get the sep position
     dwEqual:=Pos('=', NameValue);
     // Check for sep
     if (dwEqual > 0) then
     begin
        // Parse into name / value
        Name:=Copy(NameValue, 1, Pred(dwEqual));
        Value:=Copy(NameValue, Succ(dwEqual), MaxWord);
     end
     else
     begin
        // Parse into name only
        Name:=NameValue;
        Value:=EmptyStr;
     end;
  end;

  function AttributeMatches(Attributes: TStringList; Query: String): Boolean;
  var   szQuery:       String;
        szName:        String;
        szValue:       String;
        dwPos:         Integer;
  begin
     // Set default result (optimistic)
     result:=True;
     // Make a local copy of query
     szQuery:=Query;
     // Seed first name/value pair
     dwPos:=Pos(';', szQuery);
     // Loop while true
     while ((dwPos > 0) and result) do
     begin
        // Parse into name / value pair
        ParsePair(Copy(szQuery, 1, Pred(dwPos)), szName, szValue);
        // Remove pair from query string
        Delete(szQuery, 1, dwPos);
        // Check name
        if (Attributes.IndexOfName(szName) < 0) then
           // Allow for blank attribute name
           result:=(Length(szName) = 0)
        else
        begin
           // Attribute name is found, check value
           if (Length(szValue) > 0) then result:=(CompareText(Attributes.Values[szName], szValue) = 0);
        end;
        // If still true then get next position
        if result then dwPos:=Pos(';', szQuery);
     end;
     // Parse out the last pair
     if ((Length(szQuery) > 0) and result) then
     begin
        // Parse into name / value pair
        ParsePair(szQuery, szName, szValue);
        // Check name
        if (Attributes.IndexOfName(szName) < 0) then
           // Allow for blank attribute name
           result:=(Length(szName) = 0)
        else
        begin
           // Attribute name is found, check value
           if (Length(szValue) > 0) then result:=(CompareText(Attributes.Values[szName], szValue) = 0);
        end;
     end;
  end;

begin

  // Default result
  result:=nil;

  // Check for child nodes
  if Node.HasChildNodes then
  begin
     // Create list for query
     AttrList:=TStringList.Create;
     // Walk all child nodes
     for dwIndex:=0 to Node.ChildNodes.Count-1 do
     begin
        // Get node name
        NodeName:=Node.ChildNodes[dwIndex].NodeName;
        // Compare against passed name
        if (CompareText(NodeName, Name) = 0) then
        begin
           // Clear attribute list
           AttrList.Clear;
           // Walk the attributes and add to list
           for dwAttr:=0 to Node.ChildNodes[dwIndex].AttributeNodes.Count-1 do
           begin
              // Get the attribute node
              AttrNode:=Node.ChildNodes[dwIndex].AttributeNodes.Nodes[dwAttr];
              // Get name and text
              AttrName:=AttrNode.NodeName;
              AttrText:=AttrNode.Text;
              // Add name and text
              AttrList.Add(Format('%s=%s', [AttrName, AttrText]));
           end;
           // Apply the query against the attribute list
           if AttributeMatches(AttrList, AttributeQuery) then
           begin
              // Found the item
              result:=Node.ChildNodes[dwIndex];
              // Finished
              break;
           end;
        end;
     end;
     // Free the list
     FreeAndNil(AttrList);
  end;

end;
0
 
LVL 9

Author Comment

by:ginsonic
ID: 13873117
Thanks for support!
0

Featured Post

Upgrade your Question Security!

Add Premium security features to your question to ensure its privacy or anonymity. Learn more about your ability to control Question Security today.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction Since I wrote the original article about Handling Date and Time in PHP and MySQL several years ago, it seemed like now was a good time to update it for object-oriented PHP.  This article does that, replacing as much as possible the pr…
Styling your websites can become very complex. Here I'll show how SASS can help you better organize, maintain and reuse your CSS code.
Viewers will learn about if statements in Java and their use The if statement: The condition required to create an if statement: Variations of if statements: An example using if statements:
HTML5 has deprecated a few of the older ways of showing media as well as offering up a new way to create games and animations. Audio, video, and canvas are just a few of the adjustments made between XHTML and HTML5. As we learned in our last micr…
Suggested Courses

839 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question