如何在Rails中输出JSON而不转义反斜杠

时间:2017-05-26 17:39:19

标签: ruby-on-rails json ruby

我需要以一种不同寻常的格式为客户输出一些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

3 个答案:

答案 0 :(得分:11)

在与同事进行一些头脑风暴之后(感谢@TheZanke),我们找到了一个可以使用本机Rails JSON输出的解决方案。

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

我们将其跟踪到ActiveSupport中的JSON编码。所有字符串最终都通过ActiveSupport::JSON.encode进行编码。我们需要找到一种方法来短路逻辑并简单地返回未编码的字符串。

首先,我们扩展了找到hereEscapedString#to_json方法。

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

然后在控制器中我们向json哈希添加一个noencode:标志。这告诉我们的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)\/"}

答案 1 :(得分:6)

默想:

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"可能有所帮助。

答案 2 :(得分:0)

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

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

表示输入{:key=>"\\/Date(0000000000000)\\/"}(已打印)

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

第二个会帮你

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

根据需要