3

正如你们中的一些人所知,我正在为 StackOverflow 聊天系统进行 XMPP (Jabber) 集成,作为使用xmpp4r 包用 Ruby 编写的 XMPP 组件。

我正在努力解决一个问题(嗯,很多问题,但目前只有一个问题:-) 我正在从聊天中获取 JSON 提要并提取消息的 HTML。我正在使用Ruby TidyHTML 绑定将 HTML 从提供的 JSON 转换为 XHTML,以便我可以将其作为 XMPP 消息发送——因为 XMPP 消息只是 XML,将 HTML 转换为 XHTMl 应该使它成为有效的 XML,我可以只要坚持到<message>节。

对于大多数消息,它工作得很好!

我的心被炸了

然而,对于其他消息,它完全窒息——XMPP 服务器关闭了流并且脚本停止了。(而小酒馆里的 rchern 和其他人很不高兴。好吧,也许不是不高兴,但他们嘲笑我。这让我很难过!)

我几乎可以肯定,由于某种原因,消息不是有效的 XML,因此 XMPP 服务器正在关闭连接,因为它在来自 Ruby 组件的 XML 流中遇到解析错误。以下是此类消息的示例:

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

(此消息恰好是对聊天室单框链接的引用)

这是Ruby给我的错误:

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 服务器之前验证(最好是更正)XML?最有可能的是,纠正它是我为 Tidy 没有生成有效 XML 的每种情况编写额外代码的问题,但我至少希望阻止脚本崩溃。那么,如何在将 XML 发送到 XMPP 服务器之前对其进行验证呢?

4

4 回答 4

3

在这种情况下,实际错误是您的&nbsp;. 根据XEP-0071第 8 节第 5 点:

XMPP Core 的第 11.1 节规定,除了 XML 规范第 4.6 节中定义的五个通用实体(即 <、>、&、' 和 ")之外的字符实体不得通过 XML 流发送. 因此,XHTML-IM 的实现不得包含预定义的 XHTML 1.0 实体,例如   -- 相反,实现必须使用 XML 规范第 4.1 节中指定的等效字符引用(即使在不明显的地方,例如包含在 'href' 属性中的 URI)。

所以这个问题不仅仅是生成格式良好的 XML,这是一个先决条件。您还需要确保您只使用第 6 节中批准的集合中的 XHTML 。

简而言之,您需要阅读 XEP-0071。

于 2010-11-04T23:17:13.337 回答
1

也许实际上使用Nokogiri转换为 XML会有所帮助?然后,您可以为 XMPP 流重新序列化。此外,如果您希望您的东西稍微扩展一点并避免内存膨胀,请切换到Blather而不是 XMPP4r。DSL也非常棒!

于 2010-11-04T21:33:15.290 回答
1

你在 *nix 上运行吗?如果是这样,我会将问题委托给作为libxml2xmllint一部分的程序。我使用一个在通过网络发送之前生成 xml 的系统;我们使用 xmllint 验证我们的 xml,如下所示:

    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”位。

于 2010-11-04T21:52:58.790 回答
1

不要使用整洁。使用HTML5 解析器,然后将它生成的 DOM 转储为 XML。如果可以生成 DOM,则每次都可以从中生成格式良好的 XML。它还具有生成与大多数现代浏览器所提供的大致相同的 DOM 的优势。

于 2010-11-04T22:00:35.560 回答