Nejlepší způsob, jak se dostat InnerXml z XElement?

hlasů
136

Jaký je nejlepší způsob, jak získat obsah smíšené bodyprvku v kódu níže? Prvek může obsahovat buď XHTML nebo text, ale jen chci, jeho obsah v řetězci formě. XmlElementTyp má InnerXmlmajetek, který je přesně to, co jsem po ní.

Kód, jak jsou psány téměř dělá, co chci, ale zahrnuje i okolní <body>... </body>prvek, který nechci.

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()
                };
Položena 06/08/2008 v 19:16
zdroj uživatelem
V jiných jazycích...                            


15 odpovědí

hlasů
192

Chtěl jsem zjistit, které z těchto navrhovaných řešení vedly nejlépe, tak jsem běžel několik srovnávacích testů. Ze zájmu jsem také ve srovnání metod LINQ k obyčejné System.Xml metody navrhl Greg. Variace byla zajímavá, a ne to, co jsem očekával, nejpomalejším metody jsou více než 3 krát pomalejší než nejrychlejší .

Výsledky objednané nejrychlejší nejpomalejší:

  1. CreateReader - Instance Hunter (0,113 sekundy)
  2. Obyčejné System.Xml - Greg Hurlman (0,134 sekundy)
  3. Agregát s řetězci zřetězení - Mike Powell (0,324 sekundy)
  4. StringBuilder - Vin (0,333 sekundy)
  5. String.join na poli - Terry (0,360 sekund)
  6. String.Concat na poli - Marcin Kosieradzki (0,364)

Metoda

jsem jeden XML dokument s 20 stejnými uzly (nazvaný ‚tip‘):

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

Čísla uvedená v sekundách výše jsou výsledkem extrakce „vnitřní XML“ z 20 uzlů, 1000 krát za sebou, a přičemž průměr (průměr) 5 běhů. I nezahrnovala čas to trvalo načíst a analyzovat XML do XmlDocument(pro System.Xml metody) nebo XDocument(pro všechny ostatní).

Algoritmy LINQ jsem použil byly: (C # - to vše vzít XElement„rodiče“ a vrátí vnitřní řetězec XML)

CreateReader:

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

return reader.ReadInnerXml();

Agregát s zřetězením řetězce:

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 na poli:

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

String.Concat na poli:

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

I zde nejsou zobrazeny „prostý staré System.Xml“ algoritmu, jak je to jen volání .InnerXml na uzlech.


Závěr

Pokud se výkon je důležitý (např spousta XML, parsované často), tak bych použít Danielovu CreateReaderpokaždé metodu . Pokud jste právě dělá několik dotazů, možná budete chtít použít výstižnější agregační metodu Mikův.

Pokud používáte XML na velkých prvků s množstvím uzlů (asi 100 je), to by asi začít vidět výhody použití StringBuilderpřes agregační metodu, ale ne více než CreateReader. Nemyslím si, že Joini Concatmetody by někdy bylo efektivnější v těchto podmínkách, protože trestem konverzi velký seznam pro velké pole (i zde zřejmý s menšími seznamy).

Odpovězeno 10/11/2009 v 00:08
zdroj uživatelem

hlasů
66

Myslím, že to je mnohem lepší způsob (ve VB, by nemělo být těžké přeložit):

Dána XElement x:

Dim xReader = x.CreateReader
xReader.MoveToContent
xReader.ReadInnerXml
Odpovězeno 18/03/2009 v 18:17
zdroj uživatelem

hlasů
17

Jak se o použití tohoto „rozšíření“ metody na XElement? pracoval pro mě!

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();
}

Nebo použijte trochu LINQ

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

    return innerXml.ToString();
}

Poznámka : Tento kód má výše použít element.Nodes()jako protiklad k element.Elements(). Velmi důležité mít na paměti rozdíl mezi těmito dvěma. element.Nodes()dává vám vše, co jako XText, XAttributeetc, ale XElementpouze jedním z prvků.

Odpovězeno 19/08/2008 v 21:29
zdroj uživatelem

hlasů
12

(Díky!), Se vší úvěru pro ty, kdo objevil a dokázal nejlepší přístup, zde je to zabalené v metodě rozšíření:

public static string InnerXml(this XNode node) {
    using (var reader = node.CreateReader()) {
        reader.MoveToContent();
        return reader.ReadInnerXml();
    }
}
Odpovězeno 04/01/2013 v 21:21
zdroj uživatelem

hlasů
9

Nech si to jednoduché a efektivní:

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • Agregát je paměť a výkon neefektivní při zřetězení struny
  • Používání Připojit ( „“, tá) používá dvakrát větší pole řetězců než Concat ... A vypadá docela divně v kódu.
  • Pomocí + = vypadá velmi zvláštní, ale zřejmě není o mnoho horší než za použití ‚+‘ - pravděpodobně budou optimalizovány tak, aby se stejným kódem, becase výsledek úkolem je nevyužita a mohou být bezpečně odstraněn kompilátorem.
  • StringBuilder je proto nezbytně nutné - a každý ví, že zbytečné „stát“ na hovno.
Odpovězeno 31/10/2009 v 18:22
zdroj uživatelem

hlasů
7

Skončil jsem s použitím takto:

Body = t.Element("body").Nodes().Aggregate("", (b, node) => b += node.ToString());
Odpovězeno 06/08/2008 v 20:36
zdroj uživatelem

hlasů
3

Osobně jsem skončil psaní InnerXmlmetodu rozšíření pomocí agregační metodu:

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

Můj klient kód je pak stejně hutný, jak to bude se starým oboru názvů System.XML:

var innerXml = myXElement.InnerXml();
Odpovězeno 17/03/2010 v 09:47
zdroj uživatelem

hlasů
2

@Greg: Zdá se, že jste upravili svou odpověď, že je úplně jiná odpověď. Ke kterému má odpověď zní ano, mohl bych to udělat pomocí System.Xml ale doufal, že dostat nohy mokré s LINQ to XML.

Nechám můj původní odpověď níže v případě, že někdo jinde se diví, proč nemůžu jen použít vlastnost .value na XElement se dostat to, co potřebuji:

@Greg: Vlastnost Hodnota zřetězí celý text obsahu veškerých podřízených uzlů. Takže v případě, že tělo element obsahuje text pouze to funguje, ale pokud obsahuje XHTML jsem si celý text spojení společně, ale žádný z tagů.

Odpovězeno 06/08/2008 v 19:25
zdroj uživatelem

hlasů
1

doc.ToString () nebo doc.ToString (SaveOptions) dělá práci. viz http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspx

Odpovězeno 13/10/2014 v 21:08
zdroj uživatelem

hlasů
1

// pomocí Regex může být rychlejší jednoduše oříznout začátek a konec element tag

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);
Odpovězeno 08/02/2014 v 05:55
zdroj uživatelem

hlasů
0
var innerXmlAsText= XElement.Parse(xmlContent)
                    .Descendants()
                    .Where(n => n.Name.LocalName == "template")
                    .Elements()
                    .Single()
                    .ToString();

Bude dělat práci za vás

Odpovězeno 25/10/2018 v 19:07
zdroj uživatelem

hlasů
0

víš? to nejlepší, co udělat, je zpět na CDATA :( im při pohledu na řešení u nás, ale myslím, že CDATA je zdaleka nejjednodušší a nejlevnější, ne nejvhodnější rozvíjet s tho

Odpovězeno 26/10/2009 v 00:39
zdroj uživatelem

hlasů
0

Jestli (Všiml jsem zbavil b + = a prostě b +)

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

může být o něco méně účinný než

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

Není 100% jistý ... ale podíval se na agregátu () a string.join () reflektor ... Já myslím, četl jsem to jako Aggregate jen připojením vrací hodnotu, takže v podstatě získáte:

string = řetězec + string

proti string.join, že má nějakou zmínku v tamní FastStringAllocation nebo něco podobného, ​​což je pro mě věc, lidé v Microsoftu mohlo dát nějaké extra nárůst výkonu tam. Samozřejmě můj .ToArray () volání mé negovat, ale jen jsem chtěl obětovat další návrh.

Odpovězeno 18/03/2009 v 17:57
zdroj uživatelem

hlasů
0

Je možné použít objekty System.Xml jmenném prostoru, aby svou práci zde namísto použití LINQ? Jak již bylo zmíněno, XmlNode.InnerXml je přesně to, co potřebujete.

Odpovězeno 06/08/2008 v 19:20
zdroj uživatelem

hlasů
-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();
}
Odpovězeno 13/08/2010 v 08:05
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more