c# كيفية الحصول على زباث من مثيل زملنود




summary c# (11)

وهنا طريقة بسيطة لقد استخدمت، عملت بالنسبة لي.

    static string GetXpath(XmlNode node)
    {
        if (node.Name == "#document")
            return String.Empty;
        return GetXpath(node.SelectSingleNode("..")) + "/" +  (node.NodeType == XmlNodeType.Attribute ? "@":String.Empty) + node.Name;
    }

هل يمكن لشخص توفير بعض التعليمات البرمجية التي من شأنها الحصول على زباث مثيل System.Xml.XmlNode؟

شكر!


لقد أنتجت فبا ل إكسيل للقيام بذلك لمشروع العمل. فإنه يخرج توابل من زباث والنص المرتبط بها من إحدى الشخصيات أو السمة. والغرض من ذلك هو السماح لمحللي الأعمال بتحديد وتعيين بعض شمل. نقدر أن هذا هو منتدى C #، ولكن يعتقد أن هذا قد يكون من الفائدة.

Sub Parse2(oSh As Long, inode As IXMLDOMNode, Optional iXstring As String = "", Optional indexes)


Dim chnode As IXMLDOMNode
Dim attr As IXMLDOMAttribute
Dim oXString As String
Dim chld As Long
Dim idx As Variant
Dim addindex As Boolean
chld = 0
idx = 0
addindex = False


'determine the node type:
Select Case inode.NodeType

    Case NODE_ELEMENT
        If inode.ParentNode.NodeType = NODE_DOCUMENT Then 'This gets the root node name but ignores all the namespace attributes
            oXString = iXstring & "//" & fp(inode.nodename)
        Else

            'Need to deal with indexing. Where an element has siblings with the same nodeName,it needs to be indexed using [index], e.g swapstreams or schedules

            For Each chnode In inode.ParentNode.ChildNodes
                If chnode.NodeType = NODE_ELEMENT And chnode.nodename = inode.nodename Then chld = chld + 1
            Next chnode

            If chld > 1 Then '//inode has siblings of the same nodeName, so needs to be indexed
                'Lookup the index from the indexes array
                idx = getIndex(inode.nodename, indexes)
                addindex = True
            Else
            End If

            'build the XString
            oXString = iXstring & "/" & fp(inode.nodename)
            If addindex Then oXString = oXString & "[" & idx & "]"

            'If type is element then check for attributes
            For Each attr In inode.Attributes
                'If the element has attributes then extract the data pair XString + Element.Name, @Attribute.Name=Attribute.Value
                Call oSheet(oSh, oXString & "/@" & attr.Name, attr.Value)
            Next attr

        End If

    Case NODE_TEXT
        'build the XString
        oXString = iXstring
        Call oSheet(oSh, oXString, inode.NodeValue)

    Case NODE_ATTRIBUTE
    'Do nothing
    Case NODE_CDATA_SECTION
    'Do nothing
    Case NODE_COMMENT
    'Do nothing
    Case NODE_DOCUMENT
    'Do nothing
    Case NODE_DOCUMENT_FRAGMENT
    'Do nothing
    Case NODE_DOCUMENT_TYPE
    'Do nothing
    Case NODE_ENTITY
    'Do nothing
    Case NODE_ENTITY_REFERENCE
    'Do nothing
    Case NODE_INVALID
    'do nothing
    Case NODE_NOTATION
    'do nothing
    Case NODE_PROCESSING_INSTRUCTION
    'do nothing
End Select

'Now call Parser2 on each of inode's children.
If inode.HasChildNodes Then
    For Each chnode In inode.ChildNodes
        Call Parse2(oSh, chnode, oXString, indexes)
    Next chnode
Set chnode = Nothing
Else
End If

End Sub

يدير عد العناصر باستخدام:

Function getIndex(tag As Variant, indexes) As Variant
'Function to get the latest index for an xml tag from the indexes array
'indexes array is passed from one parser function to the next up and down the tree

Dim i As Integer
Dim n As Integer

If IsArrayEmpty(indexes) Then
    ReDim indexes(1, 0)
    indexes(0, 0) = "Tag"
    indexes(1, 0) = "Index"
Else
End If
For i = 0 To UBound(indexes, 2)
    If indexes(0, i) = tag Then
        'tag found, increment and return the index then exit
        'also destroy all recorded tag names BELOW that level
        indexes(1, i) = indexes(1, i) + 1
        getIndex = indexes(1, i)
        ReDim Preserve indexes(1, i) 'should keep all tags up to i but remove all below it
        Exit Function
    Else
    End If
Next i

'tag not found so add the tag with index 1 at the end of the array
n = UBound(indexes, 2)
ReDim Preserve indexes(1, n + 1)
indexes(0, n + 1) = tag
indexes(1, n + 1) = 1
getIndex = 1

End Function

جون الصحيح أن هناك أي عدد من تعبيرات زباث التي سوف تسفر عن نفس العقدة في مستند مثيل. أبسط طريقة لبناء تعبير الذي ينتج بشكل لا لبس فيه عقدة محددة هي سلسلة من عقدة الاختبارات التي تستخدم موقف العقدة في المسند، على سبيل المثال:

/node()[0]/node()[2]/node()[6]/node()[1]/node()[2]

من الواضح أن هذا التعبير لا يستخدم أسماء العناصر، ولكن بعد ذلك إذا كان كل ما تحاول القيام به هو تحديد عقدة داخل مستند، لا تحتاج إلى اسمها. كما أنه لا يمكن استخدامها للعثور على سمات (لأن السمات ليست العقد وليس لها موقف؛ يمكنك العثور عليها فقط بالاسم)، ولكنها سوف تجد جميع أنواع العقدة الأخرى.

لبناء هذا التعبير، تحتاج إلى كتابة أسلوب إرجاع موضع العقدة في العقد التابعة الأصل، لأن XmlNode لا يعرض ذلك كخاصية:

static int GetNodePosition(XmlNode child)
{
   for (int i=0; i<child.ParentNode.ChildNodes.Count; i++)
   {
       if (child.ParentNode.ChildNodes[i] == child)
       {
          // tricksy XPath, not starting its positions at 0 like a normal language
          return i + 1;
       }
   }
   throw new InvalidOperationException("Child node somehow not found in its parent's ChildNodes property.");
}

(ربما يكون هناك طريقة أكثر أناقة للقيام بذلك باستخدام لينق، منذ XmlNodeList تنفذ IEnumerable ، ولكن أنا ذاهب مع ما أعرف هنا.)

ثم يمكنك كتابة طريقة عودية مثل هذا:

static string GetXPathToNode(XmlNode node)
{
    if (node.NodeType == XmlNodeType.Attribute)
    {
        // attributes have an OwnerElement, not a ParentNode; also they have
        // to be matched by name, not found by position
        return String.Format(
            "{0}/@{1}",
            GetXPathToNode(((XmlAttribute)node).OwnerElement),
            node.Name
            );            
    }
    if (node.ParentNode == null)
    {
        // the only node with no parent is the root node, which has no path
        return "";
    }
    // the path to a node is the path to its parent, plus "/node()[n]", where 
    // n is its position among its siblings.
    return String.Format(
        "{0}/node()[{1}]",
        GetXPathToNode(node.ParentNode),
        GetNodePosition(node)
        );
}

كما ترون، أنا اختراق في وسيلة للعثور على سمات كذلك.

وانزلق جون في إصداره بينما كنت أكتب الألغام. هناك شيء حول التعليمات البرمجية التي من شأنها أن تجعل لي رانت قليلا الآن، وأنا أعتذر مقدما إذا كان يبدو وكأنه أنا خرقة على جون. (أنا لست متأكدا من أن قائمة الأشياء التي يجب أن يتعلمها جون مني قصيرة جدا). لكنني أعتقد أن النقطة التي سأقوم بها هي مهمة مهمة لكل من يعمل مع شمل إلى فكر.

أظن أن حل جون نشأ من شيء أرى الكثير من المطورين القيام به: التفكير في وثائق شمل كأشجار العناصر والصفات. وأعتقد أن هذا يأتي إلى حد كبير من المطورين الذين يستخدمون الأساسي من شمل هو شكل تسلسل، لأن كل شمل انهم تستخدم لاستخدامها منظم بهذه الطريقة. يمكنك اكتشاف هؤلاء المطورين لأنهم يستخدمون العبارة "العقدة" و "العنصر" بالتبادل. وهذا يؤدي بهم إلى التوصل إلى حلول تعالج جميع أنواع العقدة الأخرى كحالات خاصة. (كنت واحدا من هؤلاء الرجال نفسي لفترة طويلة جدا.)

هذا يبدو وكأنه افتراض مبسط بينما كنت جعله. لكنها ليست كذلك. فإنه يجعل المشاكل أكثر صعوبة ورمز أكثر تعقيدا. فإنه يقودك لتجاوز أجزاء من تكنولوجيا شمل (مثل node() وظيفة في زباث) التي تم تصميمها خصيصا لعلاج جميع أنواع العقدة بشكل عام.

هناك علم أحمر في رمز جون من شأنه أن يجعلني الاستعلام عنه في مراجعة التعليمات البرمجية حتى لو لم أكن أعرف ما هي المتطلبات، وهذا هو GetElementsByTagName . كلما رأيت هذه الطريقة قيد الاستخدام، فإن السؤال الذي يقفز إلى الذهن هو دائما "لماذا يجب أن يكون عنصرا؟" والجواب هو في كثير من الأحيان "أوه، هل يحتاج هذا الرمز للتعامل مع العقد النصية أيضا؟"


ليس هناك شيء مثل "و" زباث عقدة. لأي عقدة معينة قد يكون هناك العديد من تعبيرات زباث التي سوف تتطابق مع ذلك.

يمكنك ربما العمل حتى شجرة لبناء تعبير الذي سوف يطابق ذلك، مع الأخذ بعين الاعتبار مؤشر عناصر معينة وما إلى ذلك، لكنه لن يكون رمز لطيف بشكل رهيب.

لماذا تحتاج هذه؟ قد يكون هناك حل أفضل.


حل آخر لمشكلتك قد يكون 'علامة' زملنوديس التي سوف تحتاج إلى التعرف لاحقا مع سمة مخصصة:

var id = _currentNode.OwnerDocument.CreateAttribute("some_id");
id.Value = Guid.NewGuid().ToString();
_currentNode.Attributes.Append(id);

والتي يمكنك تخزينها في قاموس على سبيل المثال. ويمكنك لاحقا تحديد العقدة باستخدام استعلام زباث:

newOrOldDocument.SelectSingleNode(string.Format("//*[contains(@some_id,'{0}')]", id));

وأنا أعلم أن هذا ليس إجابة مباشرة على سؤالك، ولكن يمكن أن تساعد إذا كان السبب الذي ترغب في معرفة زباث عقدة هو أن يكون وسيلة من 'الوصول' عقدة في وقت لاحق بعد أن فقدت الإشارة إليها في التعليمات البرمجية .

وهذا يتغلب أيضا على المشاكل عندما تحصل الوثيقة على العناصر المضافة / المنقولة، والتي يمكن أن تعطل زباث (أو الفهارس، كما هو مقترح في إجابات أخرى).


ماذا عن استخدام تمديد الطبقة؟ ؛) نسختي (بناء على عمل الآخرين) يستخدم اسم سينتاكس [الفهرس] ... مع مؤشر حذف هو عنصر ليس لديه "الإخوة". الحلقة للحصول على مؤشر العنصر هو خارج في روتين مستقل (أيضا امتداد الطبقة).

فقط الماضي ما يلي في أي فئة المرافق (أو في فئة البرنامج الرئيسي)

static public int GetRank( this XmlNode node )
{
    // return 0 if unique, else return position 1...n in siblings with same name
    try
    {
        if( node is XmlElement ) 
        {
            int rank = 1;
            bool alone = true, found = false;

            foreach( XmlNode n in node.ParentNode.ChildNodes )
                if( n.Name == node.Name ) // sibling with same name
                {
                    if( n.Equals(node) )
                    {
                        if( ! alone ) return rank; // no need to continue
                        found = true;
                    }
                    else
                    {
                        if( found ) return rank; // no need to continue
                        alone = false;
                        rank++;
                    }
                }

        }
    }
    catch{}
    return 0;
}

static public string GetXPath( this XmlNode node )
{
    try
    {
        if( node is XmlAttribute )
            return String.Format( "{0}/@{1}", (node as XmlAttribute).OwnerElement.GetXPath(), node.Name );

        if( node is XmlText || node is XmlCDataSection )
            return node.ParentNode.GetXPath();

        if( node.ParentNode == null )   // the only node with no parent is the root node, which has no path
            return "";

        int rank = node.GetRank();
        if( rank == 0 ) return String.Format( "{0}/{1}",        node.ParentNode.GetXPath(), node.Name );
        else            return String.Format( "{0}/{1}[{2}]",   node.ParentNode.GetXPath(), node.Name, rank );
    }
    catch{}
    return "";
}   

 public static string GetFullPath(this XmlNode node)
        {
            if (node.ParentNode == null)
            {
                return "";
            }
            else
            {
                return $"{GetFullPath(node.ParentNode)}\\{node.ParentNode.Name}";
            }
        }

حسنا، لم أتمكن من مقاومة وجود الذهاب في ذلك. انها سوف تعمل فقط للسمات والعناصر، ولكن مهلا ... ماذا يمكن أن تتوقع في 15 دقيقة :) وبالمثل قد يكون هناك جيدا جدا أن يكون وسيلة أكثر نظافة للقيام بذلك.

لا لزوم لإدراج المؤشر على كل عنصر (وخاصة الجذر واحد!) ولكن الأمر أسهل من محاولة معرفة ما إذا كان هناك أي غموض خلاف ذلك.

using System;
using System.Text;
using System.Xml;

class Test
{
    static void Main()
    {
        string xml = @"
<root>
  <foo />
  <foo>
     <bar attr='value'/>
     <bar other='va' />
  </foo>
  <foo><bar /></foo>
</root>";
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(xml);
        XmlNode node = doc.SelectSingleNode("//@attr");
        Console.WriteLine(FindXPath(node));
        Console.WriteLine(doc.SelectSingleNode(FindXPath(node)) == node);
    }

    static string FindXPath(XmlNode node)
    {
        StringBuilder builder = new StringBuilder();
        while (node != null)
        {
            switch (node.NodeType)
            {
                case XmlNodeType.Attribute:
                    builder.Insert(0, "/@" + node.Name);
                    node = ((XmlAttribute) node).OwnerElement;
                    break;
                case XmlNodeType.Element:
                    int index = FindElementIndex((XmlElement) node);
                    builder.Insert(0, "/" + node.Name + "[" + index + "]");
                    node = node.ParentNode;
                    break;
                case XmlNodeType.Document:
                    return builder.ToString();
                default:
                    throw new ArgumentException("Only elements and attributes are supported");
            }
        }
        throw new ArgumentException("Node was not in a document");
    }

    static int FindElementIndex(XmlElement element)
    {
        XmlNode parentNode = element.ParentNode;
        if (parentNode is XmlDocument)
        {
            return 1;
        }
        XmlElement parent = (XmlElement) parentNode;
        int index = 1;
        foreach (XmlNode candidate in parent.ChildNodes)
        {
            if (candidate is XmlElement && candidate.Name == element.Name)
            {
                if (candidate == element)
                {
                    return index;
                }
                index++;
            }
        }
        throw new ArgumentException("Couldn't find element within parent");
    }
}

إذا قمت بذلك، سوف تحصل على مسار مع أسماء دير العقد والموقف، إذا كان لديك العقد مع نفس الاسم مثل هذا: "/ خدمة [1] / نظام [1] / مجموعة [1] / مجلد [2] ] / ملف [2] "

public string GetXPathToNode(XmlNode node)
{         
    if (node.NodeType == XmlNodeType.Attribute)
    {             
        // attributes have an OwnerElement, not a ParentNode; also they have             
        // to be matched by name, not found by position             
        return String.Format("{0}/@{1}", GetXPathToNode(((XmlAttribute)node).OwnerElement), node.Name);
    }
    if (node.ParentNode == null)
    {             
        // the only node with no parent is the root node, which has no path
        return "";
    }

    //get the index
    int iIndex = 1;
    XmlNode xnIndex = node;
    while (xnIndex.PreviousSibling != null && xnIndex.PreviousSibling.Name == xnIndex.Name)
    {
         iIndex++;
         xnIndex = xnIndex.PreviousSibling; 
    }

    // the path to a node is the path to its parent, plus "/node()[n]", where
    // n is its position among its siblings.         
    return String.Format("{0}/{1}[{2}]", GetXPathToNode(node.ParentNode), node.Name, iIndex);
}

هذا هو أسهل

 ''' <summary>
    ''' Gets the full XPath of a single node.
    ''' </summary>
    ''' <param name="node"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetXPath(ByVal node As Xml.XmlNode) As String
        Dim temp As String
        Dim sibling As Xml.XmlNode
        Dim previousSiblings As Integer = 1

        'I dont want to know that it was a generic document
        If node.Name = "#document" Then Return ""

        'Prime it
        sibling = node.PreviousSibling
        'Perculate up getting the count of all of this node's sibling before it.
        While sibling IsNot Nothing
            'Only count if the sibling has the same name as this node
            If sibling.Name = node.Name Then
                previousSiblings += 1
            End If
            sibling = sibling.PreviousSibling
        End While

        'Mark this node's index, if it has one
        ' Also mark the index to 1 or the default if it does have a sibling just no previous.
        temp = node.Name + IIf(previousSiblings > 0 OrElse node.NextSibling IsNot Nothing, "[" + previousSiblings.ToString() + "]", "").ToString()

        If node.ParentNode IsNot Nothing Then
            Return GetXPath(node.ParentNode) + "/" + temp
        End If

        Return temp
    End Function

وأنا أعلم، وظيفة قديمة ولكن النسخة أعجبت أكثر (واحد مع أسماء) كان معيبا: عندما عقدة الأم عقدا مع أسماء مختلفة، توقفت عن عد الفهرس بعد أن وجدت أول اسم العقدة غير مطابقة.

هنا هو بلدي نسخة ثابتة منه:

/// <summary>
/// Gets the X-Path to a given Node
/// </summary>
/// <param name="node">The Node to get the X-Path from</param>
/// <returns>The X-Path of the Node</returns>
public string GetXPathToNode(XmlNode node)
{
    if (node.NodeType == XmlNodeType.Attribute)
    {
        // attributes have an OwnerElement, not a ParentNode; also they have             
        // to be matched by name, not found by position             
        return String.Format("{0}/@{1}", GetXPathToNode(((XmlAttribute)node).OwnerElement), node.Name);
    }
    if (node.ParentNode == null)
    {
        // the only node with no parent is the root node, which has no path
        return "";
    }

    // Get the Index
    int indexInParent = 1;
    XmlNode siblingNode = node.PreviousSibling;
    // Loop thru all Siblings
    while (siblingNode != null)
    {
        // Increase the Index if the Sibling has the same Name
        if (siblingNode.Name == node.Name)
        {
            indexInParent++;
        }
        siblingNode = siblingNode.PreviousSibling;
    }

    // the path to a node is the path to its parent, plus "/node()[n]", where n is its position among its siblings.         
    return String.Format("{0}/{1}[{2}]", GetXPathToNode(node.ParentNode), node.Name, indexInParent);
}




.net-2.0