Ruby FastJsonAPI 动态 set_type?
Ruby FastJsonAPI dynamic set_type?
我真的一直在挖掘 FastJsonAPI。效果很好。
是否可以在每个对象的基础上设置 set_type
serializer definition?
即我正在使用 Rails STI(单一 table 继承)。我有一组混合的基础对象和派生对象,我希望每个对象都有不同的类型。
这是我想要的 JSON 输出的假示例:
{
"data": [
{
"attributes": {
"title": "Generic Vehicle"
},
"id": "1",
"type": "vehicle"
},
{
"attributes": {
"title": "Fast Car"
},
"id": "2",
"type": "car"
},
{
"attributes": {
"title": "Slow Car"
},
"id": "3",
"type": "car"
},
{
"attributes": {
"title": "Motorcycle"
},
"id": "4",
"type": "motorcycle"
}
]
}
我确实有一个可以使用的对象 type
属性,当然,因为我使用的是 STI。但是我不想将它用作属性:我想将它用作外部type
,如上面JSON[=22] =]
序列化器:
class VehicleSerializer
include FastJsonapi::ObjectSerializer
set_type :vehicle # can I tie this to individual objects, right here?
attributes :title
end
class CarSerializer < VehicleSerializer
set_type :car
attributes :title
end
class MotorcycleSerializer < VehicleSerializer
set_type :motorcycle
attributes :title
end
class TruckSerializer < VehicleSerializer
set_type :truck
attributes :title
end
你看,我有一些控制器只能从单个对象 type
中拉出,对它们来说,单个 CarSerializer
或其他任何东西都很好用。问题是当我使用这样的控制器时,它在索引方法中聚合了多种车辆类型:
require_relative '../serializers/serializers.rb'
class MultiVehiclesController < ApplicationController
def index
@vehicles = Vehicle.where(type: ["Car", "Motorcycle"])
# perhaps there's a way to modify the following line to use a different serializer for each item in the rendered query?
render json: VehicleSerializer.new(@vehicles).serializable_hash
end
def show
@vehicle = Vehicle.find(params[:id])
# I suppose here as well:
render json: VehicleSerializer.new(@vehicle).serializable_hash
end
end
这在我为此使用的 FastJsonAPI 版本 1 中是不可能的。 It is now apparently possible as of version 1.2(虽然我还没有测试)
我也有 STI 并且想在渲染不同 类 的集合时使用正确的 type
。我最终得到了一个自定义序列化程序来覆盖 hash_for_collection
。在该方法中,可以查找特定的集合项序列化程序并调用其 record_hash
这比 fast_jsonapi / jsonapi-serializer 实现有点慢,但现在 data
集合中的每个项目都有适当的 type
class MixedCollectionSerializer < ApplicationSerializer
include SerializerResolverHelper
def hash_for_collection
serializable_hash = {}
data = []
included = []
fieldset = @fieldsets[self.class.record_type.to_sym]
@resource.each do |record|
record_klazz = jsonapi_serializer_class_resolver(record,false)
data << record_klazz.record_hash(record, fieldset, @includes, @params)
included.concat record_klazz.get_included_records(record, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
end
serializable_hash[:data] = data
serializable_hash[:included] = included if @includes.present?
serializable_hash[:meta] = @meta if @meta.present?
serializable_hash[:links] = @links if @links.present?
serializable_hash
end
end
module SerializerResolverHelper
def jsonapi_serializer_class_resolver(resource, is_collection)
if resource.respond_to?(:first)
# if it is a collection and it contailns different types
first_item_class = resource.first.class
return MixedCollectionSerializer if resource.any? { |item| item.class != first_item_class }
end
JSONAPI::Rails.serializer_class(resource, is_collection)
rescue NameError
# if use STI it is necessary to resolve serializers manually
resource = resource.first if is_collection && resource.respond_to?(:first)
resource_name = case
when resource.is_a?(Vehicle) then resource.type.to_s
# another STI parent classes ...
end
"#{resource_name}Serializer".constantize if resource_name.present?
end
end
我真的一直在挖掘 FastJsonAPI。效果很好。
是否可以在每个对象的基础上设置 set_type
serializer definition?
即我正在使用 Rails STI(单一 table 继承)。我有一组混合的基础对象和派生对象,我希望每个对象都有不同的类型。
这是我想要的 JSON 输出的假示例:
{
"data": [
{
"attributes": {
"title": "Generic Vehicle"
},
"id": "1",
"type": "vehicle"
},
{
"attributes": {
"title": "Fast Car"
},
"id": "2",
"type": "car"
},
{
"attributes": {
"title": "Slow Car"
},
"id": "3",
"type": "car"
},
{
"attributes": {
"title": "Motorcycle"
},
"id": "4",
"type": "motorcycle"
}
]
}
我确实有一个可以使用的对象 type
属性,当然,因为我使用的是 STI。但是我不想将它用作属性:我想将它用作外部type
,如上面JSON[=22] =]
序列化器:
class VehicleSerializer
include FastJsonapi::ObjectSerializer
set_type :vehicle # can I tie this to individual objects, right here?
attributes :title
end
class CarSerializer < VehicleSerializer
set_type :car
attributes :title
end
class MotorcycleSerializer < VehicleSerializer
set_type :motorcycle
attributes :title
end
class TruckSerializer < VehicleSerializer
set_type :truck
attributes :title
end
你看,我有一些控制器只能从单个对象 type
中拉出,对它们来说,单个 CarSerializer
或其他任何东西都很好用。问题是当我使用这样的控制器时,它在索引方法中聚合了多种车辆类型:
require_relative '../serializers/serializers.rb'
class MultiVehiclesController < ApplicationController
def index
@vehicles = Vehicle.where(type: ["Car", "Motorcycle"])
# perhaps there's a way to modify the following line to use a different serializer for each item in the rendered query?
render json: VehicleSerializer.new(@vehicles).serializable_hash
end
def show
@vehicle = Vehicle.find(params[:id])
# I suppose here as well:
render json: VehicleSerializer.new(@vehicle).serializable_hash
end
end
这在我为此使用的 FastJsonAPI 版本 1 中是不可能的。 It is now apparently possible as of version 1.2(虽然我还没有测试)
我也有 STI 并且想在渲染不同 类 的集合时使用正确的 type
。我最终得到了一个自定义序列化程序来覆盖 hash_for_collection
。在该方法中,可以查找特定的集合项序列化程序并调用其 record_hash
这比 fast_jsonapi / jsonapi-serializer 实现有点慢,但现在 data
集合中的每个项目都有适当的 type
class MixedCollectionSerializer < ApplicationSerializer
include SerializerResolverHelper
def hash_for_collection
serializable_hash = {}
data = []
included = []
fieldset = @fieldsets[self.class.record_type.to_sym]
@resource.each do |record|
record_klazz = jsonapi_serializer_class_resolver(record,false)
data << record_klazz.record_hash(record, fieldset, @includes, @params)
included.concat record_klazz.get_included_records(record, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
end
serializable_hash[:data] = data
serializable_hash[:included] = included if @includes.present?
serializable_hash[:meta] = @meta if @meta.present?
serializable_hash[:links] = @links if @links.present?
serializable_hash
end
end
module SerializerResolverHelper
def jsonapi_serializer_class_resolver(resource, is_collection)
if resource.respond_to?(:first)
# if it is a collection and it contailns different types
first_item_class = resource.first.class
return MixedCollectionSerializer if resource.any? { |item| item.class != first_item_class }
end
JSONAPI::Rails.serializer_class(resource, is_collection)
rescue NameError
# if use STI it is necessary to resolve serializers manually
resource = resource.first if is_collection && resource.respond_to?(:first)
resource_name = case
when resource.is_a?(Vehicle) then resource.type.to_s
# another STI parent classes ...
end
"#{resource_name}Serializer".constantize if resource_name.present?
end
end