如何用Django REST Framework序列化器优雅实现嵌套JSON反序列化扁平化
DRF反序列化嵌套JSON扁平化的优雅实现方案
你需要的类似序列化阶段的点语法source写法完全可以实现,提供两种适配不同场景的方案:
方案1:单次场景快速实现
适合只需要处理个别接口的场景,不需要额外封装,直接重写to_internal_value方法完成结构转换,字段原生验证逻辑完全保留:
class TheFlattener(serializers.Serializer): # 直接定义最终要的扁平字段 name = serializers.CharField(max_length=50) lat = serializers.FloatField() lon = serializers.FloatField() def to_internal_value(self, data): # 先把输入的嵌套结构转成和序列化器字段匹配的扁平结构 flattened_data = { "name": data.get("objectName"), "lat": data.get("geoInfo", {}).get("latitude"), "lon": data.get("geoInfo", {}).get("longitude"), } # 调用父类逻辑走正常的字段验证 return super().to_internal_value(flattened_data) def create(self, validated_data): return Thing(**validated_data)
方案2:通用场景封装,支持和序列化一致的点语法source
适合需要频繁处理嵌套反序列化的场景,封装通用基类后,就可以和序列化阶段一样用点语法指定嵌套字段路径,不需要额外写转换逻辑:
第一步:封装通用扁平化反序列化基类
class FlattenDeserializer(serializers.Serializer): def to_internal_value(self, data): processed_data = {} for field_name, field in self.fields.items(): # 读取source配置的路径,默认和字段名一致 source_path = field.source if field.source != field_name else field_name # 按点拆分路径,逐层取值 value = data for key in source_path.split("."): if not isinstance(value, dict) or key not in value: value = None break value = value[key] processed_data[field_name] = value # 传入转换后的扁平数据走原生验证逻辑 return super().to_internal_value(processed_data)
第二步:业务序列化器直接继承使用
写法和序列化阶段完全一致,没有额外学习成本:
class TheFlattener(FlattenDeserializer): # 直接用点语法指定输入数据的嵌套路径 name = serializers.CharField(max_length=50, source="objectName") lat = serializers.FloatField(source="geoInfo.latitude") lon = serializers.FloatField(source="geoInfo.longitude") def create(self, validated_data): # validated_data已经是扁平化结构,直接使用即可 return Thing(**validated_data)
两种方案都不需要再定义嵌套序列化器,也不需要在create方法里做额外的字段弹出和映射,和原有实现相比代码更简洁,逻辑更清晰。
内容的提问来源于stack exchange,提问作者binnev
相关产品推荐
相关产品推荐

