จะแทนที่ to_json ใน Rails ได้อย่างไร?


94

อัปเดต:

ปัญหานี้ไม่ได้รับการสำรวจอย่างเหมาะสม render :jsonปัญหาที่แท้จริงอยู่ภายใน

การวางโค้ดแรกในคำถามเดิมจะให้ผลลัพธ์ที่คาดหวัง อย่างไรก็ตามยังมีข้อแม้ ดูตัวอย่างนี้:

render :json => current_user

คือไม่ได้เช่นเดียวกับ

render :json => current_user.to_json

นั่นคือrender :jsonจะไม่เรียกใช้to_jsonเมธอดที่เกี่ยวข้องกับวัตถุ User โดยอัตโนมัติ ในความเป็นจริงหากto_jsonถูกแทนที่บนUserโมเดลrender :json => @userจะสร้างตามที่ArgumentErrorอธิบายไว้ด้านล่าง

สรุป

# works if User#to_json is not overridden
render :json => current_user

# If User#to_json is overridden, User requires explicit call
render :json => current_user.to_json

ทั้งหมดนี้ดูเหมือนโง่สำหรับฉัน สิ่งนี้ดูเหมือนจะบอกฉันว่าrenderว่าไม่ได้โทรจริงๆModel#to_jsonเมื่อ:jsonระบุประเภท ใครช่วยอธิบายได้ว่าเกิดอะไรขึ้นที่นี่?

genii ใด ๆ ที่สามารถช่วยฉันได้อาจตอบคำถามอื่นของฉัน: วิธีสร้างการตอบสนอง JSON โดยรวม @ foo.to_json (ตัวเลือก) และ @ bar.to_json (ตัวเลือก) ใน Rails


คำถามเดิม:

ฉันเคยเห็นตัวอย่างอื่น ๆ ใน SO แต่ฉันไม่ได้ทำในสิ่งที่ฉันกำลังมองหา

ฉันกำลังพยายาม:

class User < ActiveRecord::Base

  # this actually works! (see update summary above)
  def to_json
    super(:only => :username, :methods => [:foo, :bar])
  end

end

ฉันกำลังArgumentError: wrong number of arguments (1 for 0)เข้าไป

/usr/lib/ruby/gems/1.9.1/gems/activesupport-2.3.5/lib/active_support/json/encoders/object.rb:4:in `to_json

ความคิดใด ๆ ?


ตัวอย่างของคุณใช้ได้กับโมเดลของฉัน ดำเนินการใด ๆusername, fooหรือbarวิธีการคาดหวังว่าข้อโต้แย้ง?
Jonathan Julian

ไม่มีusernameไม่ได้เป็นวิธีการและfooและbarไม่จำเป็นต้องใช้วิธีการ ฉันอัปเดตคำถามเพื่อดูว่าข้อผิดพลาดเกิดขึ้นที่ใด
maček

ฉันใช้ 1.8.7 คุณจะต้องเปิดไฟล์นั้นและดูว่าเหตุใดจึงส่ง arg ไปยัง method ที่คาดว่าจะมี args เป็นศูนย์
Jonathan Julian

คำตอบ:


217

คุณได้รับArgumentError: wrong number of arguments (1 for 0)เนื่องจากto_jsonจำเป็นต้องถูกแทนที่ด้วยพารามิเตอร์เดียวคือoptionsแฮช

def to_json(options)
  ...
end

คำอธิบายนานto_json,as_jsonและการแสดงผล:

ใน ActiveSupport 2.3.3 as_jsonถูกเพิ่มเพื่อแก้ไขปัญหาเช่นเดียวกับที่คุณพบ การสร้าง json ควรแยกจากการเรนเดอร์ของ json

ตอนนี้ทุกเวลาto_jsonถูกเรียกบนวัตถุas_jsonถูกเรียกใช้เพื่อสร้างโครงสร้างข้อมูลจากนั้นแฮชนั้นจะถูกเข้ารหัสเป็นสตริง JSON โดยใช้ActiveSupport::json.encodeใช้ สิ่งนี้เกิดขึ้นกับทุกประเภท: วัตถุตัวเลขวันที่สตริง ฯลฯ (ดูรหัส ActiveSupport)

วัตถุ ActiveRecord ทำงานในลักษณะเดียวกัน มีค่าเริ่มต้นas_jsonใช้งานที่สร้างแฮชที่รวมคุณลักษณะทั้งหมดของโมเดล คุณควรแทนที่as_jsonในรุ่นของคุณเพื่อสร้างโครงสร้าง JSON ที่คุณต้องการ as_jsonเช่นเดียวกับแบบเก่าto_jsonใช้แฮชตัวเลือกที่คุณสามารถระบุแอตทริบิวต์และวิธีการที่จะรวมไว้อย่างเปิดเผย

def as_json(options)
  # this example ignores the user's options
  super(:only => [:email, :handle])
end

ในตัวควบคุมของคุณrender :json => oสามารถยอมรับสตริงหรือวัตถุ หากเป็นสตริงจะถูกส่งผ่านเป็นส่วนตอบสนองหากเป็นอ็อบเจ็กต์to_jsonเรียกซึ่งทริกเกอร์as_jsonดังที่อธิบายไว้ข้างต้น

ดังนั้นตราบใดที่โมเดลของคุณแสดงด้วยการas_jsonแทนที่อย่างถูกต้อง(หรือไม่) โค้ดคอนโทรลเลอร์ของคุณที่จะแสดงโมเดลหนึ่งควรมีลักษณะดังนี้:

format.json { render :json => @user }

คุณธรรมของเรื่องนี้คือหลีกเลี่ยงการโทรto_jsonโดยตรงยอมให้renderทำเพื่อคุณ หากคุณต้องการปรับแต่งเอาต์พุต JSON ให้โทรas_json.

format.json { render :json => 
    @user.as_json(:only => [:username], :methods => [:avatar]) }

@ Jonathan as_jsonจูเลียนนี้เป็นคำอธิบายที่เป็นประโยชน์มาก ดังที่คุณเห็นในเอกสาร ActiveRecord :: Serialization ( api.rubyonrails.org/classes/ActiveRecord/… ) เอกสาร(ไม่) สำหรับเรื่องนี้มีน้อยมาก ฉันจะลองดู :)
maček

1
@ โจนาธานจูเลียนถ้าฉันสามารถโหวตได้ 10 ครั้งฉันจะทำ เอกสารอยู่ที่ไหนas_json! ขอบคุณอีกครั้ง :)
maček

71

หากคุณมีปัญหากับสิ่งนี้ใน Rails 3 ให้ลบล้างserializable_hashแทนas_jsonแทนสิ่งนี้จะได้รับการจัดรูปแบบ XML ของคุณฟรีด้วย :)

สิ่งนี้ทำให้ฉันต้องคิดตลอดไป หวังว่าจะช่วยใครบางคน


1
มีใครรู้วิธีการเขียนที่ดีเกี่ยวกับวิธีการนี้serializable_hashหรือไม่? เมื่อฉันใช้มันจะเปลี่ยนเอาต์พุต xml ที่ตามมาของฉันจากการตัดอ็อบเจกต์ด้วยชื่อของมัน (เช่น "quote" สำหรับ quote object ") มาเป็น" <hash> "แทนเสมอ
Tyler Collier

@TylerCollier ควรเป็นตัวเลือกเดียวกับto_xml
Sam Soffes

ขอบคุณสำหรับการแก้ปัญหานี้! ฉันใช้ ruby2 / rail4 และ as_json ไม่ทำงานกับอ็อบเจ็กต์ที่ซ้อนกันวิธีการแทนที่ไม่ได้ถูกเรียกใน 'รวม' ด้วย serializable_hash มันใช้งานได้!
santuxus

โปรดดูที่robots.thoughtbot.com/better-serialization-less-as-jsonสำหรับคำอธิบายว่าเหตุใดจึงควรเขียนทับ serializable_hash แทน
Topher Hunt

36

สำหรับผู้ที่ไม่ต้องการเพิกเฉยต่อตัวเลือกของผู้ใช้ แต่ยังเพิ่มของพวกเขา:

def as_json(options)
  # this example DOES NOT ignore the user's options
  super({:only => [:email, :handle]}.merge(options))
end

หวังว่านี่จะช่วยทุกคน :)


1
นี่เป็นวิธีที่ฉันทำยกเว้นฉันเริ่มต้นoptionsแฮชด้วย= {}ดังนั้นจึงไม่จำเป็นต้องใช้เมื่อโทร
mroach

4

แทนที่ไม่ใช่ to_json แต่เป็น as_json และจาก as_json เรียกสิ่งที่คุณต้องการ:

ลองสิ่งนี้:

def as_json 
 { :username => username, :foo => foo, :bar => bar }
end

ไม่ใช่as_jsonแค่สำหรับ ActiveResource ใช่หรือไม่
Jonathan Julian

เห็นได้ชัดว่า ActiveRecord :: Serialization มี as_json api.rubyonrails.org/classes/ActiveRecord/Serialization.html
glebm

@glebm ฉันลองแล้วก็ได้ผลลัพธ์เหมือนกัน ฉันอัปเดตคำถามเพื่อแสดงให้คุณเห็น
maček

@glebm ฉันยังคงได้รับข้อผิดพลาดเหมือนเดิม แม้ว่าฉันจะrender :json => current_userได้รับผลลัพธ์เริ่มต้นที่คาดไว้ (แอตทริบิวต์ทั้งหมดสำหรับUserโมเดลในรูปแบบ JSON) เมื่อฉันเพิ่มas_jsonวิธีการลงในUserโมเดลของฉันและลองสิ่งเดียวกันฉันได้รับข้อผิดพลาด :(
maček

@glebm ขอบคุณครับ. ฉันรู้ว่าฉันทำอะไรผิด คุณควรตรวจสอบคำถามที่อัปเดตแล้ว
maček
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.