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

Интересно, почему никто не упомянул вариант PERLDB_OPTS, названный RemotePort; хотя, по общему признанию, в Интернете мало рабочих примеров (RemotePort даже не упоминается в perldebug ) - и для меня было довольно проблематично придумать это, но здесь оно (это пример Linux).

Чтобы сделать правильный пример, сначала мне понадобилось что-то, что могло бы сделать очень простое моделирование веб-сервера CGI, желательно через одну командную строку. После нахождения Простой веб-сервер командной строки для запуска cgis. (perlmonks.org) , я нашел для этого теста IO :: All - A Tiny Web Server .

Здесь я буду работать в каталог /tmp; сценарий CGI будет /tmp/test.pl (см. ниже). Обратите внимание, что сервер IO::All будет обслуживать только исполняемые файлы в том же каталоге, что и CGI, поэтому здесь требуется chmod +x test.pl. Итак, чтобы выполнить обычный тестовый прогон CGI, я меняю каталог на /tmp в терминале и запускаю там один веб-сервер:

$ cd /tmp
$ perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'

Команда webserver будет блокироваться в терминале, и в противном случае запустит веб-сервер локально (на 127.0.0.1 или localhost) - впоследствии я могу перейти в веб-браузер и запросить этот адрес:

http://127.0.0.1:8080/test.pl

... и я должен обратите внимание на print s, сделанные с помощью test.pl, загружаемых - и показаны - в веб-браузере.


Теперь, чтобы отладить этот скрипт с RemotePort, сначала нам понадобится слушателя в сети, через который мы будем взаимодействовать с отладчиком Perl; мы можем использовать инструмент командной строки netcat (nc, увиденный здесь: Perl 如何 удаленная отладка? ). Итак, сначала запустите прослушиватель netcat в одном терминале, где он будет блокировать и ждать подключения на порту 7234 (который будет нашим портом отладки):

$ nc -l 7234

Затем нам нужно perl для запуска в режиме отладки с RemotePort, когда test.pl был вызван (даже в режиме CGI, через сервер). Это можно сделать в Linux, используя следующий сценарий «shebang wrapper», который также должен находиться в /tmp, а должен быть выполнен :

cd /tmp

cat > perldbgcall.sh <<'EOF'
#!/bin/bash
PERLDB_OPTS="RemotePort=localhost:7234" perl -d -e "do '$@'"
EOF

chmod +x perldbgcall.sh

Это своего рода сложная вещь - см. Сценарий оболочки - Как я могу использовать переменные среды в моем shebang? - Unix & amp; Linux Stack Exchange . Но трюк здесь, кажется, не , чтобы развить интерпретатор perl, который обрабатывает test.pl - поэтому, как только мы его ударим, мы не exec, но вместо этого мы вызываем perl «прямо» и в основном «источник» нашего скрипта test.pl с использованием do (см. . Как запустить скрипт Perl из скрипта Perl? ).

Теперь что у нас есть perldbgcall.sh в /tmp - мы можем изменить файл test.pl, так что он ссылается на этот исполняемый файл на своей строке shebang (вместо обычного интерпретатора Perl) - здесь /tmp/test.pl изменено таким образом:

#!./perldbgcall.sh

# this is test.pl

use 5.10.1;
use warnings;
use strict;

my $b = '1';
my $a = sub { "hello $b there" };
$b = '2';
print "YEAH " . $a->() . " CMON\n";
$b = '3';
print "CMON " . &$a . " YEAH\n";

$DB::single=1;  # BREAKPOINT

$b = '4';
print "STEP " . &$a . " NOW\n";
$b = '5';
print "STEP " . &$a . " AGAIN\n";

Теперь оба test.pl и его новый обработчик shebang, perldbgcall.sh, находятся в /tmp; и мы nc слушаем отладочные соединения на порту 7234 - поэтому мы можем, наконец, открыть другое окно терминала, сменить каталог на /tmp и запустить веб-сервер с одним слоем (который будет прослушивать веб-соединения на порту 8080):

cd /tmp
perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'

После этого мы можем перейти в наш веб-браузер и запросить тот же адрес, http://127.0.0.1:8080/test.pl. Однако теперь, когда веб-сервер пытается выполнить скрипт, он сделает это через perldbgcall.sh shebang - который запустит perl в режиме удаленного отладчика. Таким образом, выполнение скрипта приостанавливается - и поэтому веб-браузер блокируется, ожидая данных. Теперь мы можем переключиться на терминал netcat, и мы должны увидеть знакомый текст отладчика Perl, однако вывести его через nc:

$ nc -l 7234

Loading DB routines from perl5db.pl version 1.32
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(-e:1):   do './test.pl'
  DB<1> r
main::(./test.pl:29):   $b = '4';
  DB<1>

. Как показывает фрагмент, теперь мы в основном используем nc как «терминал», поэтому мы можем ввести r (и Enter) для «run» - и скрипт будет запускаться с помощью инструкции breakpoint (см. также В perl, в чем разница между $ DB: : single = 1 и 2? ), прежде чем снова остановиться (обратите внимание, что в этот момент браузер все равно заблокируется).

Итак, теперь мы можем, скажем, пройти через остальные test.pl через терминал nc:

....
main::(./test.pl:29):   $b = '4';
  DB<1> n
main::(./test.pl:30):   print "STEP " . &$a . " NOW\n";
  DB<1> n
main::(./test.pl:31):   $b = '5';
  DB<1> n
main::(./test.pl:32):   print "STEP " . &$a . " AGAIN\n";
  DB<1> n
Debugged program terminated.  Use q to quit or R to restart,
  use o inhibit_exit to avoid stopping after program termination,
  h q, h R or h o to get additional info.
  DB<1>

... однако, также в этот момент браузер блокирует и ждет данных. Только после выхода из отладчика с q:

  DB<1> q
$

... блокирует блокировку браузера и, наконец, отображает (полный) вывод test.pl:

YEAH hello 2 there CMON
CMON hello 3 there YEAH
STEP hello 4 there NOW
STEP hello 5 there AGAIN

Конечно, такой отладочный процесс можно сделать даже без запуска веб-сервера, однако, в этом случае, мы не трогаем веб-сервер; мы запускаем выполнение «изначально» (для CGI) из веб-браузера - и единственным изменением, необходимым для самого скрипта CGI, является изменение shebang (и, конечно же, наличие скрипта оболочки shebang в качестве исполняемого файла в том же директория).

Ну, надеюсь, это поможет кому-то - я бы наверняка любил бы наткнуться на это, вместо того, чтобы писать сам :) Cheers!

143
задан Amirhossein Mehrvarzi 30 May 2015 в 08:04
поделиться

11 ответов

Я хотел посмотреть, какое из предложенных решений работает лучше всего, поэтому провел несколько сравнительных тестов. Ради интереса я также сравнил методы 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 on array - Терри (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());

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


Заключение

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

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


Заключение

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

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


Заключение

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

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

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

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

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

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

s более краткий метод Aggregate.

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

s более краткий метод Aggregate.

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

204
ответ дан 23 November 2019 в 22:52
поделиться

Действительно ли возможно использовать объекты пространства имен System.Xml сделать задание здесь вместо того, чтобы использовать LINQ? Поскольку Вы уже упомянули, XmlNode. InnerXml точно, в чем Вы нуждаетесь.

0
ответ дан Greg Hurlman 30 May 2015 в 08:04
поделиться
  • 1
    Да, I' m думающий о преобразовании моего класса к структуре, которую я тогда или встраиваю в другой класс или происхожу из. – SmacL 11 June 2010 в 09:31

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

Body = t.Element("body").Nodes().Aggregate("", (b, node) => b += node.ToString());
7
ответ дан Jeff Atwood 30 May 2015 в 08:04
поделиться
  • 1
    Если TR1 доступен, он может использовать станд.:: tr1:: has_trivial_copy и т.д. – Derek Ledbetter 12 June 2010 в 02:30

Как насчет того, чтобы использовать этот "дополнительный" метод на 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 только Элемент.

18
ответ дан Markus Safar 30 May 2015 в 08:04
поделиться
  • 1
    @Neil, I' m знающий об этом. I' ve отредактировал мой ответ так для удаления возможного неправильного чтения. – Kirill V. Lyadvinsky 11 June 2010 в 10:48

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

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

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

2
ответ дан Mike Powell 30 May 2015 в 08:04
поделиться

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

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

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

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

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

строка = представляют в виде строки + строка

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

0
ответ дан 30 May 2015 в 08:04
поделиться
  • 1
    У меня был он то же и да, прием должен запустить VS как Администратора. Спасибо за совместное использование Вы сохранили меня много времени! – Jean-François 31 August 2013 в 17:19

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

, Учитывая XElement x:

Dim xReader = x.CreateReader
xReader.MoveToContent
xReader.ReadInnerXml
69
ответ дан Instance Hunter 30 May 2015 в 08:04
поделиться

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

0
ответ дан 23 November 2019 в 22:52
поделиться

Сохраняйте простоту и эффективность:

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

Лично я закончил тем, что написал метод расширения 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
ответ дан 23 November 2019 в 22:52
поделиться
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
ответ дан 23 November 2019 в 22:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: