如何防止 Ruby plist 将 ' 转换为 &#39?

How to prevent Ruby plist converting ' to &#39?

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

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

谢谢!

plist gem 使用 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还是return&#39;?原来是right after the method definition,包含了一个叫cgi/escape的库:

  begin
    require 'cgi/escape'
  rescue LoadError
  end

猜猜是什么 - 出于性能原因,cgi/escape 被具有 hard-coded 值的 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,这可能会产生不良副作用。

一种侵入性较小的方法是使用自定义 escapeHTML 方法在 Plist 命名空间下实现一个 CGI 模块:(因此只有 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。 (您可能想打开一个功能请求)