【问题标题】:How to output JSON in Rails without escaping back slashes如何在 Rails 中输出 JSON 而不转义反斜杠
【发布时间】:2017-10-27 16:29:56
【问题描述】:

我需要以某种不寻常的格式为客户输出一些 JSON。我的应用是用 Rails 5 编写的。

所需的 JSON:

{
  "key": "\/Date(0000000000000)\/"
}

时间戳值需要在字符串的开头和结尾都有一个\/。据我所知,这似乎是.NET services 中常用的一种格式。我一直试图让斜线正确输出。

我将问题简化为具有单个控制器操作的 vanilla Rails 5 应用程序。到目前为止,我能想到的所有逃逸排列都失败了。

def index
  render json: {
    a: '\/Date(0000000000000)\/',
    b: "\/Date(0000000000000)\/",
    c: '\\/Date(0000000000000)\\/',
    d: "\\/Date(0000000000000)\\/"
  }
end

输出如下:

{
    "a": "\\/Date(0000000000000)\\/",
    "b": "/Date(0000000000000)/",
    "c": "\\/Date(0000000000000)\\/",
    "d": "\\/Date(0000000000000)\\/"
}

为了便于讨论,假设格式受第三方控制,无法更改。

我已经上传了一个测试应用到 Github 来演示这个问题。 https://github.com/gregawoods/test_app_ignore_me

【问题讨论】:

  • 他们的请求表明缺乏理解解释字符串如何与转义字符一起工作,而您的实验表明您正在了解它。您得到的输出是正确的。 "/value/""\/value\/" 相同,因为 Ruby 将 "\/" 视为转义的正斜杠,不需要转义,因此它删除了反斜杠。 "\\" 是我们在双引号字符串中创建反斜杠的方式。在单引号字符串中,它是'\'
  • 是的,我倾向于同意这一点,我只是想在不摇摆不定的情况下满足要求。我可能会尝试渲染/value/ 以查看他们的解析器是否会接受它。作为旁注,以下提到日期时间的答案让我认为这可能是 .NET 的事情:stackoverflow.com/questions/1580647/…

标签: ruby-on-rails json ruby


【解决方案1】:

在与同事进行了一些头脑风暴(感谢 @TheZanke)后,我们找到了一个适用于原生 Rails JSON 输出的解决方案。

警告:此代码覆盖 ActiveSupport 中的一些核心行为。使用风险自负,并应用明智的单元测试!

我们将此追溯到 ActiveSupport 中的 JSON 编码。所有字符串最终都通过ActiveSupport::JSON.encode 编码。我们需要找到一种方法来缩短该逻辑并简单地返回未编码的字符串。

首先我们扩展了EscapedString#to_json 方法找到here

module EscapedStringExtension
  def to_json(*)
    if starts_with?('noencode:')
      "\"#{self}\"".gsub('noencode:', '')
    else
      super
    end
  end
end

module ActiveSupport::JSON::Encoding
  class JSONGemEncoder
    class EscapedString
      prepend EscapedStringExtension
    end
  end
end

然后在控制器中,我们将noencode: 标志添加到 json 哈希中。这告诉我们的to_json 版本不要进行任何额外的编码。

def index
  render json: {
     a: '\/Date(0000000000000)\/',
     b: 'noencode:\/Date(0000000000000)\/',
   }
end

渲染的输出显示b 提供了我们想要的,而a 保留了标准行为。

$ curl http://localhost:3000/sales/index.json
{"a":"\\/Date(0000000000000)\\/","b":"\/Date(0000000000000)\/"}

【讨论】:

  • 为什么不直接使用 render text: my_method(string) 并在该方法中执行所有这些有趣的操作,而不是覆盖 ActiveSupport 方法?
【解决方案2】:

思考这个:

Ruby 在双引号和单引号字符串中对正斜杠的处理方式相同。

"/"   # => "/"
'/'   # => "/"

在双引号字符串中"\/" 表示\ 正在转义以下字符。因为/ 没有转义等效项,所以会产生一个正斜杠:

"\/"  # => "/"

在所有情况下,在单引号字符串中,除了一个,它意味着有一个反斜杠,后跟字符的文字值。这种情况是当你想代表一个反斜杠本身时:

'\/'  # => "\\/"

"\\/" # => "\\/"
'\\/' # => "\\/"

了解这一点是在语言中处理字符串最令人困惑的部分之一,而且这不仅限于 Ruby,它来自于编程的早期阶段。

了解以上内容:

require 'json'

puts JSON[{ "key": "\/value\/" }] 
puts JSON[{ "key": '/value/' }]
puts JSON[{ "key": '\/value\/' }]

# >> {"key":"/value/"}
# >> {"key":"/value/"}
# >> {"key":"\\/value\\/"}

您应该能够更清楚地了解您在结果和上面的 JSON 输出中看到的内容。

我认为这个规则最初是为 C 创建的,所以“Escape sequences in C”可能会有所帮助。

【讨论】:

  • 感谢您的建议。我对转义的概念很熟悉——正是这个特定的用例让我很难过。
  • 好吧,如果您在代码中生成值,只需使用单引号来定义字面量,结果就会正确。如果客户不理解,您必须将他们指向文档。或者向他们收取额外的辅导费用。
【解决方案3】:

嗨,我认为这是最简单的方法

.gsub("/",'//').gsub('\/','')

用于输入{:key=>"\\/Date(0000000000000)\\/"}(打印)

第一个 gsub 会做{"key":"\\//Date(0000000000000)\\//"}

秒会得到你

{"key":"\/Date(0000000000000)\/"}

如你所愿

【讨论】:

  • 不幸的是,这也不起作用。我最终在 JSON 输出中得到 /Date(0000000000000)/
  • 您是否尝试将 json 渲染为原始字符串?
  • 是和不是。渲染一个原始字符串确实可以让我们输出我们需要的值。但是,这不是一种可以很好地扩展以生成大量复杂 JSON 的策略。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-04
  • 2017-05-01
  • 1970-01-01
  • 2012-10-03
  • 2015-03-29
  • 1970-01-01
相关资源
最近更新 更多