Используя Ruby, как я могу подтвердить правильность фрагмента XML? - PullRequest
3 голосов
/ 05 ноября 2010

Как некоторые из вас знают, я работаю над интеграцией XMPP (Jabber) для системы чата StackOverflow , как компонент XMPP, написанный на Ruby с использованием пакета xmpp4r .

Я борюсь с одной проблемой (ну, много проблем, но одна проблема на данный момент :-) Я беру канал JSON из чата и извлекаю HTML для сообщений. Я использую привязки Ruby TidyHTML для преобразования HTML из JSON, переданного в XHTML, чтобы я мог отправить его в виде сообщения XMPP - поскольку сообщения XMPP являются просто XML, преобразование HTML в XHTMl должно сделать это действительный XML, который я могу просто вставить в раздел <message>.

Для большинства сообщений , это прекрасно работает!

My Mind Is Blown

Однако для других сообщений он полностью задыхается - сервер XMPP закрывает поток, и сценарий останавливается. (И Рчерн, и другие в «Таверне» расстраиваются. Ну, может, не расстроен , но они смеются надо мной. Это меня огорчает!)

Я почти уверен, что по той или иной причине сообщения не являются действительными XML-данными, и поэтому сервер XMPP закрывает соединение, поскольку обнаруживает ошибку разбора в потоке XML из компонента Ruby. Вот пример одного такого сообщения:

<message to='jeswah@smart-safe-secure.com/Token' type='groupchat' xmlns='jabber:client'><body>&lt;div class=&quot;onebox ob-message&quot;&gt;&lt;a class=&quot;roomname&quot; href=&quot;/transcript/message/263372#263372&quot;&gt;&lt;span title=&quot;2010-11-04 19:20:23Z&quot;&gt;1 hour ago&lt;/span&gt;&lt;/a&gt;, by &lt;span class=&quot;user-name&quot;&gt;Fosco&lt;/span&gt; &lt;br/&gt;&lt;div class=&quot;quote&quot;&gt;&lt;div class=&quot;room-mini&quot;&gt;&lt;div class=&quot;room-mini-header&quot;&gt;&lt;h3&gt;&lt;img class=&quot;small-site-logo&quot; title=&quot;Gaming&quot; alt=&quot;Gaming&quot; width=&quot;16&quot; height=&quot;16&quot; src=&quot;http://sstatic.net/gaming/img/favicon.ico&quot; /&gt;&amp;nbsp;&lt;span class=&quot;room-name&quot;&gt;&lt;a href=&quot;http://chat.stackexchange.com/rooms/28/minecraft-talk&quot;&gt;Minecraft Talk&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;div class=&quot;room-mini-description&quot;&gt;Everything Minecraft, including classic and survival mode&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;room-current-user-count&quot; title=&quot;current users&quot;&gt;9&lt;/div&gt;&lt;div class=&quot;mspark&quot; style=&quot;height:25px;width:205px&quot;&gt;
&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:13px;left:0px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:9px;left:8px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:2px;left:16px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:8px;left:24px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:1px;left:32px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:1px;left:56px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:0px;left:64px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:0px;left:88px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:0px;left:96px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:11px;left:104px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:7px;left:112px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:7px;left:120px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:25px;left:128px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:14px;left:136px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:4px;left:144px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:7px;left:152px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:19px;left:160px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:19px;left:168px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:12px;left:176px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar&quot; style=&quot;width:8px;height:11px;left:184px;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mspbar now&quot; style=&quot;height:25px;left:154px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;clear-both&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</body><html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'><div class="onebox ob-message"><a class="roomname" href="/transcript/message/263372#263372"><span title="2010-11-04 19:20:23Z">1 hour ago</span></a>, by <span class="user-name">Fosco</span><br />
<div class="quote">
<div class="room-mini"><div class="room-mini-header">
<h3><img class="small-site-logo" title="Gaming" alt="Gaming" width="16" height="16" src="http://sstatic.net/gaming/img/favicon.ico" />&nbsp;<span class="room-name"><a href="http://chat.stackexchange.com/rooms/28/minecraft-talk">Minecraft Talk</a></span></h3>
<div class="room-mini-description">Everything Minecraft, including classic and survival mode</div>
</div>
<div class="room-current-user-count" title="current users">9</div>
<div class="mspark" style="height:25px;width:205px">
<div class="mspbar" style="width:8px;height:13px;left:0px;"></div>
<div class="mspbar" style="width:8px;height:9px;left:8px;"></div>
<div class="mspbar" style="width:8px;height:2px;left:16px;"></div>
<div class="mspbar" style="width:8px;height:8px;left:24px;"></div>
<div class="mspbar" style="width:8px;height:1px;left:32px;"></div>
<div class="mspbar" style="width:8px;height:1px;left:56px;"></div>
<div class="mspbar" style="width:8px;height:0px;left:64px;"></div>
<div class="mspbar" style="width:8px;height:0px;left:88px;"></div>
<div class="mspbar" style="width:8px;height:0px;left:96px;"></div>
<div class="mspbar" style="width:8px;height:11px;left:104px;"></div><div class="mspbar" style="width:8px;height:7px;left:112px;"></div><div class="mspbar" style="width:8px;height:7px;left:120px;"></div><div class="mspbar" style="width:8px;height:25px;left:128px;"></div><div class="mspbar" style="width:8px;height:14px;left:136px;"></div>
<div class="mspbar" style="width:8px;height:4px;left:144px;"></div>
<div class="mspbar" style="width:8px;height:7px;left:152px;"></div>
<div class="mspbar" style="width:8px;height:19px;left:160px;"></div>
<div class="mspbar" style="width:8px;height:19px;left:168px;"></div><div class="mspbar" style="width:8px;height:12px;left:176px;"></div>
<div class="mspbar" style="width:8px;height:11px;left:184px;"></div>
<div class="mspbar now" style="height:25px;left:154px;"></div>
</div>
<div class="clear-both"></div>
</div>
</div>
</div>
</body></html></message>

(Это сообщение было цитатой из ссылки в чате на один ящик)

Вот ошибка, которую Руби дал мне:

IOError: stream closed
/usr/lib/ruby/1.8/xmpp4r/stream.rb:594:in `empty?'
/usr/lib/ruby/1.8/rexml/parsers/baseparser.rb:153:in `empty?'
/usr/lib/ruby/1.8/rexml/parsers/baseparser.rb:193:in `pull'
/usr/lib/ruby/1.8/rexml/parsers/sax2parser.rb:92:in `parse'
/usr/lib/ruby/1.8/xmpp4r/streamparser.rb:79:in `parse'
/usr/lib/ruby/1.8/xmpp4r/stream.rb:75:in `start'
/usr/lib/ruby/1.8/xmpp4r/stream.rb:72:in `initialize'
/usr/lib/ruby/1.8/xmpp4r/stream.rb:72:in `new'
/usr/lib/ruby/1.8/xmpp4r/stream.rb:72:in `start'
/usr/lib/ruby/1.8/xmpp4r/connection.rb:119:in `start'
/usr/lib/ruby/1.8/xmpp4r/component.rb:70:in `start'
/usr/lib/ruby/1.8/xmpp4r/connection.rb:77:in `connect'
/usr/lib/ruby/1.8/xmpp4r/component.rb:47:in `connect'
./classes/SOXMPP_Bridge.rb:20:in `initialize'
./soxmpp.rb:81:in `new'
./soxmpp.rb:81

Наконец-то мой вопрос!

Учитывая, что отправка неверного XML на сервер XMPP выводит меня из себя, есть ли способ, используя Ruby, я могу проверить (и, предпочтительно, исправить ) XML перед отправкой его на сервер XMPP? Скорее всего, исправляя , я буду писать дополнительный код для каждого случая, когда Tidy не выдает корректный XML, но я бы по крайней мере хотел бы остановить сбой сценария. Итак, как я могу проверить XML перед отправкой на сервер XMPP?

Ответы [ 4 ]

3 голосов
/ 05 ноября 2010

Фактическая ошибка в этом случае - ваша &nbsp;. Согласно XEP-0071 , раздел 8, пункт 5:

Раздел 11.1 Ядра XMPP предусматривает, что символьные объекты, кроме пяти общих объектов, определенных в Разделе 4.6 спецификации XML (т. Е. & Lt ;, & gt ;, & amp ;, & amp; поток XML. Поэтому реализации XHTML-IM НЕ ДОЛЖНЫ включать предопределенные объекты XHTML 1.0, такие как & nbsp; - вместо этого реализации ДОЛЖНЫ использовать ссылки на эквивалентные символы, как указано в разделе 4.1 спецификации XML (даже в неочевидных местах, таких как URI, которые включены в атрибут 'href').

Так что эта проблема больше, чем просто создание правильно сформированного XML, что является обязательным условием. Вам также нужно убедиться, что вы используете только XHTML из утвержденного набора в section 6 .

Короче, вам нужно прочитать XEP-0071.

1 голос
/ 05 ноября 2010

Вы работаете на * nix? Если это так, я бы делегировал проблему xmllint, программе, которая является частью libxml2 . Я работаю с системой, которая генерирует XML перед отправкой по сети; мы проверяем наш xml с помощью xmllint, вот так:

    command = "xmllint #{temp_file_path} --schema #{schema_file_path} --noout 2>&1"
    output = `#{command}`
    if $? != 0
      temp_dir.keep
      $stderr.puts "Error validating xml: running command #{command.inspect}"
      $stderr.puts output
      exit(1)
    end

Вам, конечно, нужно будет адаптировать это к вашей ситуации, но основная идея работает хорошо. Если у вас нет DTD, пропустите бит «--schema».

1 голос
/ 05 ноября 2010

Не используйте Tidy. Используйте HTML5 парсер , затем выведите DOM, который он генерирует, в XML. Если вы можете создать DOM, вы можете каждый раз создавать из него правильно сформированный XML. Он также будет иметь преимущество в создании примерно того же DOM, что и большинство современных браузеров.

1 голос
/ 05 ноября 2010

Может быть, на самом деле преобразование в XML с Nokogiri поможет?Затем вы можете повторно сериализовать поток XMPP.Кроме того, если вы хотите, чтобы ваши вещи немного масштабировались и избегали раздувания памяти, переключитесь на Blather вместо XMPP4r.Также DSL довольно круто!

...