Подтвердить что ты не робот

Как выводить JSON в Rails без экранирования обратных косых черт

Мне нужно вывести некоторый JSON для клиента в несколько необычном формате. Мое приложение написано с помощью Rails 5.

Желаемый JSON:

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

Значение метки времени должно иметь \/ как в начале, так и в конце строки. Насколько я могу судить, это формат, обычно используемый в сервисах .NET. Я застрял, пытаясь правильно вывести косые черты.

Я уменьшил проблему до приложения 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

4b9b3361

Ответ 1

После некоторого мозгового штурма с коллегами (спасибо @TheZanke), мы столкнулись с решением, которое работает с исходным выходом Rails JSON.

ПРЕДУПРЕЖДЕНИЕ: Этот код переопределяет некоторые основные действия в ActiveSupport. Используйте на свой страх и риск и применяйте разумное модульное тестирование!

Мы отследили это до кодировки JSON в ActiveSupport. Все строки в конечном итоге кодируются через ActiveSupport::JSON.encode. Нам нужно было найти способ короткого замыкания этой логики и просто вернуть незакодированную строку.

Сначала мы расширили метод EscapedString#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

Затем в контроллере мы добавляем флаг 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)\/"}

Ответ 2

Размышляйте над этим:

Ruby рассматривает forward-slashes одно и то же в строках с двойными кавычками и одинарными кавычками.

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

В строке с двойными кавычками "\/" означает, что \ выполняет следующий символ. Поскольку / не имеет экранированного эквивалента, он приводит к одной прямой косой чертой:

"\/"  # => "/"

В одной кавычки во всех случаях, кроме одного, это означает, что обратный слэш, за которым следует буквальное значение символа. Это единственный случай, когда вы хотите представить обратную косую черту:

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

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

Изучение этого является одной из самых запутанных частей, связанных со строками в языках, и это не ограничивается Ruby, это что-то с первых дней программирования.

Зная это:

require 'json'

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

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

вы должны уметь больше понимать, что вы видите в своих результатах и ​​в выводе JSON выше.

Я думаю, что правила для этого были первоначально созданы для C, поэтому "" Escape sequence in C" может помочь.

Ответ 3

Привет, я думаю, что это самый простой способ

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

для ввода {:key=>"\\/Date(0000000000000)\\/"} (напечатан)

первый gsub выполнит {"key":"\\//Date(0000000000000)\\//"}

секунд вы получите

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

по мере необходимости