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

голоса
136

Какой самый лучший способ , чтобы получить содержимое смешанного 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()
                };
Задан 06/08/2008 в 19:16
источник пользователем
На других языках...                            


15 ответов

голоса
192

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

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

  1. CreateReader - Экземпляр Хантер (0.113 секунд)
  2. Обычный старый System.Xml - Грег Hurlman (0.134 секунд)
  3. Совокупный с конкатенацией - Майк Пауэлл (0,324 секунд)
  4. StringBuilder - Vin (0.333 секунд)
  5. String.join на массиве - Терри (0.360 секунд)
  6. String.Concat на массиве - Marcin Kosieradzki (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());

Я не показал алгоритм «Plain старый System.Xml» здесь как это просто вызов .InnerXml на узлах.


Вывод

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

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

Ответил 10/11/2009 в 00:08
источник пользователем

голоса
66

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

Дано XElement х:

Dim xReader = x.CreateReader
xReader.MoveToContent
xReader.ReadInnerXml
Ответил 18/03/2009 в 18:17
источник пользователем

голоса
17

Как об использовании этого «расширение» метод на 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/08/2008 в 21:29
источник пользователем

голоса
12

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

public static string InnerXml(this XNode node) {
    using (var reader = node.CreateReader()) {
        reader.MoveToContent();
        return reader.ReadInnerXml();
    }
}
Ответил 04/01/2013 в 21:21
источник пользователем

голоса
9

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

String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
  • Совокупное это память и производительность неэффективна при конкатенации строк
  • Использование Join ( «», СТГ) использует в два раза больше, чем массив строк Concat ... И выглядит довольно странно в коде.
  • Используя + = выглядит очень странно, но, видимо, не намного хуже, чем при использовании «+» - вероятно, будет оптимизирована для того же кода, Becase результат присваивания не используется и может быть безопасно удален компилятором.
  • StringBuilder так важно - и все знают, что нет необходимости «государство» отстой.
Ответил 31/10/2009 в 18:22
источник пользователем

голоса
7

Я в конечном итоге с помощью этого:

Body = t.Element("body").Nodes().Aggregate("", (b, node) => b += node.ToString());
Ответил 06/08/2008 в 20:36
источник пользователем

голоса
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();
Ответил 17/03/2010 в 09:47
источник пользователем

голоса
2

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

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

@Greg: Свойство Значения объединяющего все текстовое содержимое всех дочерних узлов. Таким образом, если тело элемент содержит только текст, он работает, но если он содержит XHTML я получаю весь текст сцепляется вместе, но ни один из тегов.

Ответил 06/08/2008 в 19:25
источник пользователем

голоса
1

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

Ответил 13/10/2014 в 21:08
источник пользователем

голоса
1

// с помощью 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);
Ответил 08/02/2014 в 05:55
источник пользователем

голоса
0
var innerXmlAsText= XElement.Parse(xmlContent)
                    .Descendants()
                    .Where(n => n.Name.LocalName == "template")
                    .Elements()
                    .Single()
                    .ToString();

Будет ли делать работу за вас

Ответил 25/10/2018 в 19:07
источник пользователем

голоса
0

вы знаете? самое лучшее, что нужно сделать, это вернуться к CDATA :( им смотреть на решения здесь, но я думаю, что CDATA, безусловно, самый простой и дешевый, не самый удобный для разработки с Тхо

Ответил 26/10/2009 в 00:39
источник пользователем

голоса
0

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

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

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

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

Не 100% уверен ... но глядя на Совокупности () и string.join () в Отражатель ... Я думаю , что я прочитал это как Совокупные просто присоединяя возвращающегося значение, так по существу вы получаете:

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

по сравнению с string.join, она имеет некоторые упоминания в там о FastStringAllocation или что-то, что делает меня вещь люди в Microsoft могли бы поставить некоторые дополнительный прирост производительности там. Конечно, мой .ToArray () называют мой NEGATE, но я просто хотел, чтобы возносить другое предложение.

Ответил 18/03/2009 в 17:57
источник пользователем

голоса
0

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

Ответил 06/08/2008 в 19:20
источник пользователем

голоса
-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();
}
Ответил 13/08/2010 в 08:05
источник пользователем

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