#ruby-on-rails #ruby
#ruby-on-rails #ruby
Вопрос:
Почему это (оценивается в консоли Rails)
[{:a => :b}].collect {|x| OpenStruct.new(x)}.to_json
добавляет туда запись «table»?
"[{"table":{"a":"b"}}]
Я хочу именно этого:
"[{"a":"b"}]
Означает ли это, что метод Rails to_json обрабатывает OpenStruct по-другому? Когда я пробую это в irb, его там нет:
require 'ostruct'
[{:a => :b}].collect {|x| OpenStruct.new(x)}.inspect
Ответ №1:
Потому что @table — это переменная экземпляра OpenStruct и Объект #as_json возвращает хэш переменных экземпляра.
В моем проекте я реализовал OpenStruct#as_json для переопределения поведения.
require "ostruct"
class OpenStruct
def as_json(options = nil)
@table.as_json(options)
end
end
Комментарии:
1. Я
OpenStruct
весь день пытался обойти методinspect
«s», и это единственное, что сработало для меня. Спасибо.2. Именно то, что мне нужно! Вы спасли мой день! Спасибо
3. Это вызывает у меня ошибки аргументации. У меня есть
Failure
иSuccess
классы, которые наследуются от OpenStruct. Чтобы было ясно, я уверен, что это работает с OpenStruct, но с другими моими классами, которые наследуют от него, это ломается. Где яnew
призываю передавать атрибуты для доступа из моих сервисов. Выдает ошибку, когда я динамически передаю атрибуты / значения новому методу
Ответ №2:
Используйте marshal_dump
, хотя это несколько противоречит цели предварительного преобразования его в OpenStruct:
[{:a => :b}].collect {|x| OpenStruct.new(x).marshal_dump }.to_json
=> "[{"a":"b"}]"
Более короткий путь был бы:
[{:a => :b}].to_json
"[{"a":"b"}]"
В качестве альтернативы вы можете использовать денежный патч OpenStruct#as_json
, как показано в ответе Хироши:
require "ostruct"
class OpenStruct
def as_json(options = nil)
@table.as_json(options)
end
end
Комментарии:
1. Да, причина, по которой я это делаю, заключается в том, что мне нужно изменить исходную структуру хэша (переименовать некоторые поля, удалить некоторые другие).
2. Работает как шарм. Если есть какой-либо лучший способ сделать это, дайте мне знать: gist.github.com/1300921
3. Есть ли недостаток
to_h.as_json(options)
, а не ссылки@table
? Кажется более безопасным использовать общедоступный интерфейс, даже если я подклассирую Orstruct .
Ответ №3:
Я решаю проблему, подклассируя OpenStruct следующим образом:
class DataStruct < OpenStruct
def as_json(*args)
super.as_json['table']
end
end
затем вы можете легко преобразовать в JSON следующим образом:
o = DataStruct.new(a:1, b:DataStruct.new(c:3))
o.to_json
# => "{"a":1,"b":{"c":3}}"
Аккуратно, да? Итак, в ответ на ваш вопрос, вы бы написали это вместо:
[{:a => :b}].collect {|x| DataStruct.new(x)}.to_json
предоставляя вам:
=> "[{"a":"b"}]"
ОБНОВЛЕНИЕ ДЛЯ RUBY 2.7 (5 февраля 2021)
require 'json'
require 'ostruct'
class OpenStruct
def to_json
to_hash.to_json
end
def to_hash
to_h.map { |k, v|
v.respond_to?(:to_hash) ? [k, v.to_hash] : [k, v]
}.to_h
end
end
o = OpenStruct.new(a:1, b:OpenStruct.new(c:3))
p o.to_json
Комментарии:
1. Обратите внимание, что я должен был сделать
super['table']
, чтобы заставить это работать.2. Что именно делает это «для Ruby 2.7»?
Ответ №4:
Я обнаружил, что другие ответы были немного запутанными, приземлившись здесь, чтобы просто выяснить, как превратить мою OpenStruct в a Hash
или JSON. Чтобы уточнить, вы можете просто вызвать marshal_dump
свой OpenStruct
.
$ OpenStruct.new(hello: :world).to_json
=> "{"table":{"hello":"world"}}"
$ OpenStruct.new(hello: :world).marshal_dump
=> {:hello=>:world}
$ OpenStruct.new(hello: :world).marshal_dump.to_json
=> "{"hello":"world"}"
Я лично не решался бы исправлять OpenStruct
ошибки, если вы не делаете это в подклассе, поскольку это может иметь непредвиденные последствия.
Комментарии:
1. Это лучший ответ! Мне это нравится
Ответ №5:
С ruby 2.1.2 вы можете использовать следующее, чтобы получить JSON без корневого элемента таблицы:
[{:a => :b}].collect {|x| OpenStruct.new(x).to_h}.to_json
=> "[{"a":"b"}]"
Ответ №6:
openstruct_array.map(amp;:to_h).as_json
Ответ №7:
Проблема здесь в том, что внутренне он выполняет a as_json
, который создает хэш с ключом таблицы (потому что as_json также сериализует все переменные экземпляра объекта и @table
является переменной экземпляра OpenStruct), а затем выполняет a to_json
для того, что его строит.
Итак, самый простой способ — сначала просто использовать to_h
(который не сериализует переменные экземпляра), а затем to_json
на этом. Итак:
OpenStruct.new(x).to_h.json
или в вашем случае open_struct_array.map(amp;:to_h).to_json