Лучший способ получить InnerXml XElement?

Какой лучший способ получить содержимое смешанного bodyэлемента в коде ниже? Элемент может содержать либо XHTML, либо текст, но я просто хочу его содержимое в виде строки. 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()
                };
6.08.2008 18:16:55
15 ОТВЕТОВ
РЕШЕНИЕ

Я хотел посмотреть, какое из этих предложенных решений работает лучше всего, поэтому я провел несколько сравнительных тестов. Из интереса я также сравнил методы LINQ с простым старым методом System.Xml, предложенным Грегом. Вариация была интересной и не такой, как я ожидал: самые медленные методы были более чем в 3 раза медленнее, чем самые быстрые .

Результаты упорядочены от самых быстрых к медленным:

  1. CreateReader - Instance Hunter (0,113 секунды)
  2. Обычный старый System.Xml - Грег Херлман (0,134 секунды)
  3. Агрегат с конкатенацией строк - Майк Пауэлл (0,324 секунды)
  4. StringBuilder - Vin (0,333 секунды)
  5. String.Join на массив - Терри (0,360 секунды)
  6. String.Concat на массиве - Марчин Косерадзки (0.364)

метод

Я использовал один XML-документ с 20 одинаковыми узлами (называемый «подсказка»):

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

Числа, показанные в секундах выше, являются результатом извлечения «внутреннего XML» из 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());

String.Concat в массиве:

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

Я не показал здесь алгоритм «Обычная старая System.Xml», так как он просто вызывает .InnerXml на узлах.


Заключение

Если важна производительность (например, много XML, часто анализируется), я бы каждый раз использовал CreateReaderметод Дэниела . Если вы просто делаете несколько запросов, возможно, вы захотите использовать более краткий метод Майка Aggregate.

Если вы используете XML на больших элементах с большим количеством узлов (возможно, 100), вы, вероятно, начнете видеть преимущества использования StringBuilderметода Aggregate, но не более CreateReader. Я не думаю , что Joinи Concatметоды когда - либо будут более эффективными в этих условиях из - за штраф преобразования большого списка большого массива (даже очевидном здесь с небольшими списками).

207
7.02.2016 17:40:29
Версия StringBuilder может быть записана в одну строку: var result = parent.Elements (). Aggregate (new StringBuilder (), (sb, xelem) => sb.AppendLine (xelem.ToString ()), sb => sb.ToString ( ))
Softlion 23.09.2011 14:27:39
Вы пропустили parent.CreateNavigator().InnerXml(нужен using System.Xml.XPathметод расширения).
Richard 23.07.2012 17:23:59
Я бы не подумал, что вам нужно .ToArray()внутри .Concat, но, кажется, это делает его быстрее
drzaus 14.01.2014 18:02:51
В случае , если вы не прокручиваются в нижней части этих ответов: рассмотреть только лишив контейнер / корень из .ToString()за этого ответа . Кажется, еще быстрее ...
drzaus 4.06.2014 17:55:36
Вы должны действительно обернуть это var reader = parent.CreateReader();в операторе использования.
BrainSlugs83 18.03.2015 22:04:31

Можно ли использовать объекты пространства имен System.Xml для выполнения работы здесь вместо использования LINQ? Как вы уже упоминали, XmlNode.InnerXml - это именно то, что вам нужно.

0
6.08.2008 18:36:39

@ Грег: Похоже, вы отредактировали свой ответ, чтобы он был совершенно другим. На что мой ответ - да, я мог бы сделать это с помощью System.Xml, но надеялся получить удовольствие от LINQ to XML.

Я оставлю свой оригинальный ответ ниже на тот случай, если кто-то еще удивится, почему я не могу просто использовать свойство .Value XElement, чтобы получить то, что мне нужно:

@Greg: свойство Value объединяет все текстовое содержимое любых дочерних узлов. Поэтому, если элемент body содержит только текст, он работает, но если он содержит XHTML, я получаю объединенный весь текст, но ни один из тегов.

2
7.08.2008 02:18:19
Я столкнулся с той же самой проблемой и подумал, что это ошибка: у меня был «смешанный» контент (т.е. <root>random text <sub1>child</sub1> <sub2>child</sub2></root>), который стал random text childchildчерезXElement.Parse(...).Value
drzaus 4.06.2014 17:45:09

Я закончил тем, что использовал это:

Body = t.Element("body").Nodes().Aggregate("", (b, node) => b += node.ToString());
7
7.08.2008 10:10:16
Это сделает много конкатенации строк - я бы предпочел, чтобы Вин сам использовал StringBuilder. Руководство foreach не является отрицательным.
Marc Gravell♦ 6.12.2008 22:33:36
Этот метод действительно спас меня сегодня, пытаясь выписать XElement с новым конструктором, и ни один из других методов не пригодился ему, а этот сделал. Спасибо!
delliottg 13.08.2014 20:42:31

Как насчет использования этого метода "extension" в 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();
}

ИЛИ использовать немного Linq

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только элемент.

19
7.02.2016 17:40:51

Интересно, если (обратите внимание, я избавился от b + = и просто есть b +)

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

может быть немного менее эффективным, чем

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

Не уверен на 100% ... но взглянув на Aggregate () и string.Join () в Reflector ... Я думаю, что я читаю это как Aggregate, просто добавляя возвращаемое значение, так что по сути вы получите:

строка = строка + строка

В отличие от string.Join, там есть упоминание о FastStringAllocation или о чем-то еще, что заставляет меня задуматься о том, что ребята из Microsoft могли бы добавить туда дополнительное повышение производительности. Конечно, мой .ToArray () называет это моим отрицанием, но я просто хотел предложить другое предложение.

0
18.03.2009 16:57:12

Я думаю, что это гораздо лучший метод (в VB его не должно быть сложно перевести):

Дан XElement x:

Dim xReader = x.CreateReader
xReader.MoveToContent
xReader.ReadInnerXml
69
18.03.2009 17:17:25
Приятно! Это намного быстрее, чем некоторые другие предложенные методы (я проверил их все - см. Мой ответ для деталей). Хотя все они выполняют свою работу, этот делает это быстрее всего - даже быстрее, чем сама System.Xml.Node.InnerXml!
Luke Sampson 9.11.2009 23:12:05
XmlReader одноразовый, поэтому не забудьте обернуть его с помощью, пожалуйста (я бы сам отредактировал ответ, если бы знал VB).
Dmitry Fedorkov 25.11.2013 12:25:06

ты знаешь? Лучше всего вернуться к CDATA :( Я смотрю на решения здесь, но я думаю, что CDATA является самым простым и дешевым, но не самым удобным для разработки с помощью

0
25.10.2009 23:39:21

Держите это простым и эффективным:

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • Агрегат неэффективен при объединении строк памяти и производительности
  • Использование Join ("", sth) использует в два раза больший массив строк, чем Concat ... И выглядит довольно странно в коде.
  • Использование + = выглядит очень странно, но, очевидно, не намного хуже, чем использование «+» - возможно, оно будет оптимизировано для того же кода, поскольку результат присваивания не используется и может быть безопасно удален компилятором.
  • StringBuilder настолько обязателен - и все знают, что ненужное «состояние» - отстой.
10
31.10.2009 17:22:59

Лично я закончил писать InnerXmlметод расширения с использованием метода Aggregate:

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

Мой клиентский код будет таким же лаконичным, как и в старом пространстве имен System.Xml:

var innerXml = myXElement.InnerXml();
3
17.03.2010 08:47:45
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();
}
-2
13.08.2010 07:05:02
А также, если элемент имеет какие-либо атрибуты или даже слишком много места, логика не работает.
Christoph 22.06.2019 23:56:11

С должным уважением к тем, кто нашел и доказал лучший подход (спасибо!), Здесь он обернут в метод расширения:

public static string InnerXml(this XNode node) {
    using (var reader = node.CreateReader()) {
        reader.MoveToContent();
        return reader.ReadInnerXml();
    }
}
15
4.01.2013 20:21:41

// использование Regex может быть быстрее, просто обрезать начальный и конечный тег элемента

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
8.02.2014 04:55:35
аккуратный. еще быстрее просто использовать IndexOf:var xml = root.ToString(); var begin = xml.IndexOf('>')+1; var end = xml.LastIndexOf('<'); return xml.Substring(begin, end-begin);
drzaus 4.06.2014 17:53:35

Работает doc.ToString () или doc.ToString (SaveOptions). См. Http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.tostring(v=vs.110).aspx

1
13.10.2014 20:08:32
Нет. Он также включает в себя элемент со всеми его атрибутами. Требуется только содержимое между начальным и конечным тегом.
Christoph 22.06.2019 23:51:56
var innerXmlAsText= XElement.Parse(xmlContent)
                    .Descendants()
                    .Where(n => n.Name.LocalName == "template")
                    .Elements()
                    .Single()
                    .ToString();

Сделаю работу за вас

0
25.10.2018 19:07:28