सबसे अच्छा तरीका है एक XElement की इनरएक्सएलएमएल प्राप्त करने के लिए?


147

bodyनीचे दिए गए कोड में मिश्रित तत्व की सामग्री प्राप्त करने का सबसे अच्छा तरीका क्या है ? तत्व में एक्सएचटीएमएल या पाठ हो सकता है, लेकिन मैं केवल इसकी सामग्री को स्ट्रिंग रूप में चाहता हूं। इस XmlElementप्रकार की InnerXmlसंपत्ति है, जो वास्तव में मेरे बाद है।

जैसा कि लिखा गया कोड लगभग वही करता है जो मैं चाहता हूं, लेकिन आस-पास <body>... </body>तत्व भी शामिल है , जो मुझे नहीं चाहिए।

XDocument doc = XDocument.Load(new StreamReader(s));
var templates = from t in doc.Descendants("template")
                where t.Attribute("name").Value == templateName
                select new
                {
                   Subject = t.Element("subject").Value,
                   Body = t.Element("body").ToString()
                };

जवाबों:


208

मैं देखना चाहता था कि इनमें से कौन-से सुझाए गए समाधानों में सबसे अच्छा प्रदर्शन किया गया है, इसलिए मैंने कुछ तुलनात्मक परीक्षण किए। रुचि के बाहर, मैंने भी ग्रेग द्वारा सुझाए गए सादे पुराने System.Xml विधि की LINQ विधियों की तुलना की । भिन्नता दिलचस्प थी और न कि मुझे क्या उम्मीद थी, जिसमें सबसे धीमी विधियां सबसे तेज से 3 गुना अधिक धीमी थीं ।

परिणाम सबसे धीमी गति से करने का आदेश दिया:

  1. क्रिएटर - इंस्टेंट हंटर (0.113 सेकंड)
  2. सादा पुराना System.Xml - ग्रेग हर्लमैन (0.134 सेकंड)
  3. स्ट्रिंग संघनन के साथ एकत्र - माइक पॉवेल (0.324 सेकंड)
  4. स्ट्रिंगबर्ल - विन (0.333 सेकंड)
  5. स्ट्रिंग.जून सरणी पर - टेरी (0.360 सेकंड)
  6. स्ट्रिंग.कोनकैट एरे पर - मार्सिन कोसिरादज़की (0.364)

तरीका

मैंने 20 समान नोड्स (जिसे 'संकेत' कहा जाता है) के साथ एकल XML दस्तावेज़ का उपयोग किया:

<hint>
  <strong>Thinking of using a fake address?</strong>
  <br />
  Please don't. If we can't verify your address we might just
  have to reject your application.
</hint>

ऊपर दिए गए सेकंड के रूप में दिखाए गए नंबर 20 नोड्स के "इनर एक्सएमएल" को निकालने, एक पंक्ति में 1000 बार और 5 रन के औसत (मतलब) लेने का परिणाम है। मैंने XML को लोड करने और पार्स करने के XmlDocumentलिए ( System.Xml विधि के लिए) या XDocument(अन्य सभी के लिए) को शामिल नहीं किया।

मेरे द्वारा उपयोग किए गए LINQ एल्गोरिदम थे: (C # - सभी एक XElement"पैरेंट" लेते हैं और आंतरिक XML स्ट्रिंग लौटाते हैं)

CreateReader:

var reader = parent.CreateReader();
reader.MoveToContent();

return reader.ReadInnerXml();

स्ट्रिंग संघनन के साथ एकत्र:

return parent.Nodes().Aggregate("", (b, node) => b += node.ToString());

StringBuilder:

StringBuilder sb = new StringBuilder();

foreach(var node in parent.Nodes()) {
    sb.Append(node.ToString());
}

return sb.ToString();

String.Join सरणी पर:

return String.Join("", parent.Nodes().Select(x => x.ToString()).ToArray());

स्ट्रिंग पर सरणी:

return String.Concat(parent.Nodes().Select(x => x.ToString()).ToArray());

मैं "सादा पुराना System.Xml" एल्गोरिथ्म यहाँ नहीं दिखा रहा हूँ क्योंकि यह सिर्फ कॉल कर रहा है। नोड्स पर .nerXml।


निष्कर्ष

यदि प्रदर्शन महत्वपूर्ण है (जैसे बहुत सारे XML, बार-बार पार्स किए गए), तो मैं हर बार डैनियल की CreateReaderविधि का उपयोग करता हूं । यदि आप कुछ सवाल कर रहे हैं, तो आप माइक के अधिक संक्षिप्त एग्रीगेट विधि का उपयोग करना चाह सकते हैं।

यदि आप बहुत सारे नोड्स (शायद 100 के) के साथ बड़े तत्वों पर एक्सएमएल का उपयोग कर रहे हैं, तो आप शायद StringBuilderएग्रीगेट विधि का उपयोग करने का लाभ देखना शुरू कर देंगे , लेकिन खत्म नहीं हुआ CreateReader। मुझे नहीं लगता कि इन स्थितियों में Joinऔर Concatविधियां कभी भी इन स्थितियों में अधिक कुशल होंगी क्योंकि एक बड़ी सूची को एक बड़ी सरणी में बदलने के लिए (यहां तक ​​कि छोटी सूचियों के साथ भी स्पष्ट है)।


StringBuilder संस्करण एक पंक्ति पर लिखा जा सकता है: var result = parent.Elements ()। एग्रीगेट (नया StringBuilder (), (sb, xelem) => sb.AppendLine (xeleb.ToString) (), sb => sb.ToString ( ))
शीतल

7
आप चूक गए parent.CreateNavigator().InnerXml( using System.Xml.XPathविस्तार विधि की आवश्यकता)।
रिचर्ड

मुझे नहीं लगा होगा कि आपको .ToArray()अंदर की जरूरत है .Concat, लेकिन यह इसे और तेज़ बनाता है
drzaus

यदि आप इन उत्तरों के नीचे स्क्रॉल नहीं करते हैं: इस उत्तर के.ToString() अनुसार कंटेनर / रूट को अलग करना उचित समझें । और भी तेज़ लगता है ...
drzaus

2
आपको वास्तव var reader = parent.CreateReader();में एक उपयोग वक्तव्य में लपेटना चाहिए ।
ब्रेनस्ल्गस83

70

मुझे लगता है कि यह एक बेहतर तरीका है (VB में, अनुवाद करने के लिए कठिन नहीं होना चाहिए):

XElement x को देखते हुए:

Dim xReader = x.CreateReader
xReader.MoveToContent
xReader.ReadInnerXml

अच्छा! यह प्रस्तावित कुछ अन्य विधियों की तुलना में बहुत तेज है (मैंने उन सभी का परीक्षण किया - विवरण के लिए मेरा उत्तर देखें)। हालांकि वे सभी काम करते हैं, यह एक सबसे तेज़ करता है - यहां तक ​​कि System.Xml.Node.InnerXx की तुलना में तेजी से जब्त करता है!
ल्यूक सैम्पसन

4
XmlReader डिस्पोजेबल है, इसलिए इसे उपयोग करने के साथ लपेटना न भूलें, कृपया (यदि मैं वीबी जानता था तो मैं खुद को जवाब संपादित करूंगा)।
दिमित्री फेडोरकोव

19

XElement पर इस "एक्सटेंशन" विधि का उपयोग करने के बारे में कैसे? मेरे लिए काम किया!

public static string InnerXml(this XElement element)
{
    StringBuilder innerXml = new StringBuilder();

    foreach (XNode node in element.Nodes())
    {
        // append node's xml string to innerXml
        innerXml.Append(node.ToString());
    }

    return innerXml.ToString();
}

या लिनेक का थोड़ा उपयोग करें

public static string InnerXml(this XElement element)
{
    StringBuilder innerXml = new StringBuilder();
    doc.Nodes().ToList().ForEach( node => innerXml.Append(node.ToString()));

    return innerXml.ToString();
}

नोट : ऊपर दिए गए कोड का element.Nodes()विरोध करने के लिए उपयोग करना है element.Elements()। दोनों के बीच अंतर को याद करने के लिए बहुत महत्वपूर्ण बात। element.Nodes()आपको सब कुछ देता है XText, XAttributeआदि, लेकिन XElementकेवल एक तत्व।


15

उन लोगों के लिए सभी उचित श्रेय के साथ जिन्होंने सर्वोत्तम दृष्टिकोण की खोज की और साबित किया (धन्यवाद!), यहाँ इसे विस्तार विधि में लपेटा गया है:

public static string InnerXml(this XNode node) {
    using (var reader = node.CreateReader()) {
        reader.MoveToContent();
        return reader.ReadInnerXml();
    }
}

10

इसे सरल और कुशल रखें:

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • जब तारों को समेटना हो तो एग्रीगेट मेमोरी और परफॉर्मेंस अक्षम्य है
  • कॉनैट की तुलना में Join ("", sth) का प्रयोग दो गुना बड़ा स्ट्रिंग ऐरे का उपयोग कर रहा है ... और कोड में काफी अजीब लग रहा है।
  • + = का उपयोग करना बहुत ही अजीब लगता है, लेकिन जाहिरा तौर पर '+' का उपयोग करने की तुलना में बहुत बुरा नहीं है - शायद एक ही कोड के लिए अनुकूलित किया जाएगा, क्योंकि असाइनमेंट परिणाम अप्रयुक्त है और सुरक्षित रूप से संकलक द्वारा हटाया जा सकता है।
  • StringBuilder बहुत जरूरी है - और हर कोई जानता है कि अनावश्यक "राज्य" बेकार है।

7

मैंने इसका उपयोग करते हुए समाप्त किया:

Body = t.Element("body").Nodes().Aggregate("", (b, node) => b += node.ToString());

यह बहुत सारे स्ट्रिंग कॉन्सेप्टेशन करेगा - मैं विन का उपयोग स्ट्रिंगब्यूरी खुद करना पसंद करूंगा। मैनुअल फ़ॉरचैट एक नकारात्मक नहीं है।
मार्क Gravell

इस विधि ने वास्तव में आज मुझे बचा लिया, नए निर्माणकर्ता के साथ एक्सईमेंट लिखने की कोशिश की और अन्य तरीकों में से कोई भी खुद को हाथ से उधार नहीं दे रहा था, जबकि यह एक था। धन्यवाद!
delliottg

3

व्यक्तिगत रूप से, मैंने InnerXmlएग्रीगेट विधि का उपयोग करके एक एक्सटेंशन विधि लिखना समाप्त किया :

public static string InnerXml(this XElement thiz)
{
   return thiz.Nodes().Aggregate( string.Empty, ( element, node ) => element += node.ToString() );
}

मेरा क्लाइंट कोड तो उसी तरह है जैसे कि यह पुराने System.Xml नाम स्थान के साथ होगा।

var innerXml = myXElement.InnerXml();

2

@Greg: ऐसा प्रतीत होता है कि आपने अपने उत्तर को पूरी तरह से अलग उत्तर दिया है। जिस पर मेरा जवाब हां है, मैं System.Xml का उपयोग करके ऐसा कर सकता था, लेकिन LINQ से XML तक मेरे पैर गीले होने की उम्मीद कर रहा था।

मैं अपना मूल उत्तर नीचे छोड़ दूंगा अगर कोई और आश्चर्यचकित हो जाए कि मैं XElement का उपयोग क्यों नहीं कर सकता। मुझे प्राप्त करने के लिए संपत्ति का उपयोग करें:

@Greg: मान गुण किसी भी बच्चे के नोड्स की सभी पाठ्य सामग्री को समाप्‍त कर देता है। इसलिए अगर बॉडी एलिमेंट में केवल टेक्स्ट ही काम करता है, लेकिन अगर इसमें एक्सएचटीएमएल है तो मुझे सभी टेक्स्ट एक साथ मिल जाते हैं, लेकिन कोई भी टैग नहीं।


मैं इस एक ही मुद्दे में भाग गया और सोचा था कि यह एक बग था: मैं था 'मिश्रित' सामग्री (यानी <root>random text <sub1>child</sub1> <sub2>child</sub2></root>) जो बन random text childchildके माध्यम सेXElement.Parse(...).Value
drzaus

1

// रेगेक्स का उपयोग करना शुरू और अंत तत्व टैग को ट्रिम करने के लिए तेज हो सकता है

var content = element.ToString();
var matchBegin = Regex.Match(content, @"<.+?>");
content = content.Substring(matchBegin.Index + matchBegin.Length);          
var matchEnd = Regex.Match(content, @"</.+?>", RegexOptions.RightToLeft);
content = content.Substring(0, matchEnd.Index);

1
साफ। और भी तेजी से उपयोग करने के लिए IndexOf:var xml = root.ToString(); var begin = xml.IndexOf('>')+1; var end = xml.LastIndexOf('<'); return xml.Substring(begin, end-begin);
drzaus

1

doc.ToString () या doc.ToString (SaveOptions) काम करता है। Http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspx देखें


नहीं, यह नहीं है। इसमें अपने सभी गुणों के साथ तत्व भी शामिल है। केवल प्रारंभ और अंतिम टैग के बीच की सामग्री चाहता है।
क्रिस्टोफ

0

क्या LINQ का उपयोग करने के बजाय यहाँ काम करने के लिए System.Xml नामस्थान वस्तुओं का उपयोग करना संभव है? जैसा कि आपने पहले ही उल्लेख किया है, XmlNode.InnerXml वही है जिसकी आपको आवश्यकता है।


0

आश्चर्य है कि अगर (मुझे b + = से छुटकारा मिल गया है और अभी b + है)

t.Element( "body" ).Nodes()
 .Aggregate( "", ( b, node ) => b + node.ToString() );

की तुलना में थोड़ा कम कुशल हो सकता है

string.Join( "", t.Element.Nodes()
                  .Select( n => n.ToString() ).ToArray() );

100% निश्चित नहीं ... लेकिन एग्रीगेट () और स्ट्रिंग.जॉइन () को रिफ्लेक्टर में देखना ... मुझे लगता है मैं इसे एग्रीगेट के रूप में पढ़ता हूं, केवल एक रिटर्निंग वैल्यू को हूं, इसलिए अनिवार्य रूप से आपको मिलता है:

string = string + स्ट्रिंग

बनाम string.Join, इसमें FastStringAllocation या कुछ और के बारे में कुछ उल्लेख किया गया है, जिससे मुझे लगता है कि लोगों ने Microsoft पर कुछ अतिरिक्त प्रदर्शन को बढ़ावा दिया है। बेशक मेरे .ToArray () मेरा नकारात्मक फोन है कि, लेकिन मैं सिर्फ एक और सुझाव देना चाहता था।


0

तुम्हे पता हैं? सबसे अच्छी बात यह है कि सीडीएटीए पर वापस जाएं :( मैं यहां समाधान देख रहा हूं लेकिन मुझे लगता है कि सीडीएटीए अब तक का सबसे सरल और सस्ता है, थियो के साथ विकसित करने के लिए सबसे सुविधाजनक नहीं है



-2
public static string InnerXml(this XElement xElement)
{
    //remove start tag
    string innerXml = xElement.ToString().Trim().Replace(string.Format("<{0}>", xElement.Name), "");
    ////remove end tag
    innerXml = innerXml.Trim().Replace(string.Format("</{0}>", xElement.Name), "");
    return innerXml.Trim();
}

और यह भी कि अगर तत्व में कोई विशेषता है या केवल एक स्थान है तो भी तर्क विफल हो जाता है।
क्रिस्टोफ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.