如何防止Ruby plist转换"到&#39?

问题描述

我使用plist gem来操作现有的plist,但是保存后发现单引号(表示为'转换为&#39),该脚本用于修改XCode项目使用的plist,该列表也可以在XCode中编辑,并且XCode将单引号保存为'

所以我的问题是:有没有办法强迫Ruby使用与XCode相同的名称?我想如果没有的话,最后的办法是添加一些行,以在运行Ruby脚本后手动将所有'转换为'

谢谢!

解决方法

plist宝石使用CGI.escapeHTML来转义特殊字符'&"<>

require 'cgi'

CGI.escapeHTML("foo'bar")
#=> "foo&#39;bar"

如果我们看一下它的implementation,我们会发现它实际上是Ruby代码。该方法基本上是使用哈希的gsub调用:(我省略了一些编码内容)

module CGI::Util
  # ...

  # The set of special characters and their escaped values
  TABLE_FOR_ESCAPE_HTML__ = {
    "'" => '&#39;','&' => '&amp;','"' => '&quot;','<' => '&lt;','>' => '&gt;',}

  # Escape special characters in HTML,namely '&\"<>
  #   CGI.escapeHTML('Usage: foo "bar" <baz>')
  #      # => "Usage: foo &quot;bar&quot; &lt;baz&gt;"
  def escapeHTML(string)
    # ...
    string.gsub(/['&\"<>]/,TABLE_FOR_ESCAPE_HTML__)
  end

  # ...
end

所以我们可以更改常数以适合我们的需求,对吧?

CGI::TABLE_FOR_ESCAPE_HTML__["'"] = "&apos;"

CGI.escapeHTML("foo'bar")
#=> "foo&#39;bar" <- ???

为什么escapeHTML仍返回&#39;?事实证明,其中包含right after the method definition(称为cgi/escape的库)

  begin
    require 'cgi/escape'
  rescue LoadError
  end

猜猜是什么-出于性能原因,cgi/escape被具有硬编码值的C implementation覆盖escapeHTML


但是,并非全部丢失。我们可以尝试将C实现恢复为原始形式:

method = CGI::Util.instance_method(:escapeHTML)
unless method.owner == CGI::Util
  method = method.super_method until method.owner == CGI::Util
  CGI::define_singleton_method(:escapeHTML,method)
end

CGI::TABLE_FOR_ESCAPE_HTML__["'"] = '&apos;'
CGI.escapeHTML("foo'bar")
#=> "foo&apos;bar"

但这将在全球范围内改变CGI.escapeHTML,这可能会带来不良后果。

一种不太麻烦的方法是使用自定义的CGI方法在Plist名称空间下实现escapeHTML模块:(因此只有plist会使用此实现)

require 'plist'

module Plist
  class CGI
    TABLE_FOR_ESCAPE_HTML__ = ::CGI::TABLE_FOR_ESCAPE_HTML__.merge("'" => '&apos;')

    def self.escapeHTML(string)
      string.gsub(/['&\"<>]/,TABLE_FOR_ESCAPE_HTML__)
    end
  end
end

puts Plist::Emit.dump("foo'bar")

输出:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<string>foo&apos;bar</string>
</plist>

gem应该避免使用CGI.escapeHTML,并提供自己的转义机制以符合Apple / XCode的要求。 (您可能要打开功能请求)