使用Ruby,我如何确认XML snippit是否有效?

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

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

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

我的思绪被吹嘘

但是对于其他消息,它完全窒息 – XMPP服务器关闭流并且脚本停止运行。 (和酒馆里的其他人一起感到不安。好吧,也许不会沮丧 ,但他们会嘲笑我。这让我感到难过!)

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

 <div class="onebox ob-message"><a class="roomname" href="https://stackoverflow.com/transcript/message/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>
1 hour ago, by Fosco

 Minecraft Talk

Everything Minecraft, including classic and survival mode
9

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

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

最后,我的问题!

鉴于向XMPP服务器发送无效的XML会让我失望,有没有办法使用Ruby我可以在将XML发送到XMPP服务器之前validation(并且最好是更正 )? 最有可能的是, 纠正它将是我为Tidy没有生成有效XML的每个案例编写额外代码的问题,但我至少想要阻止脚本崩溃。 那么,如何在将XML发送到XMPP服务器之前validationXML呢?

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

XMPP Core的第11.1节规定,XML规范的第4.6节中定义的五个通用实体以外的字符实体(即&lt;,&gt;,&amp;,’,和’)绝不能通过XML流发送。 因此,XHTML-IM的实现绝不能包含预定义的XHTML 1.0实体,例如&nbsp; – 相反,实现必须使用XML规范的第4.1节中指定的等效字符引用(即使在非显而易见的地方,例如’href’属性中包含的URI)。

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

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

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

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

你在* nix上运行吗? 如果是这样,我xmllint问题委托给xmllint ,这是一个libxml2的一部分程序。 我使用一个生成xml的系统,然后通过网络发送它; 我们使用xmllintvalidation我们的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”位。