scala - 使用 circe 解码各种类型列表的问题
问题描述
我有一个 json 字符串,它是一个 json 对象列表,每个 json 都有几个嵌入对象;主要问题在于
自定义字段
,该字段是一个列表,它可以有多种类型的对象;我在此页面上找到了一个示例https://immutables.pl/2017/02/25/customizing-circes-auto-generic-derivation/,但是我对 circe 和 shapeless 没有太多经验,当我尝试做它的解码向我发送了这个异常:
Left(DecodingFailure(CNil, List(DownArray, DownField(custom_fields), DownArray, DownField(tasks))))
解码代码库:
import io.circe
import io.circe.generic.extras.{AutoDerivation, Configuration}
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import io.circe.{Decoder, Encoder}
object ADT extends App {
val json =
"""
| {
| "tasks": [
| {
| "id": "xxxxxxx",
| "custom_id": null,
| "name": "xxxxxxxxxxxxxxxxx",
| "text_content": null,
| "description": null,
| "status": {
| "status": "Closed",
| "color": "#6bc950",
| "type": "closed",
| "orderindex": 3
| },
| "orderindex": "247.00000000000000000000000000000000",
| "date_created": "1581725281039",
| "date_updated": "1587696872148",
| "date_closed": "1583380816440",
| "archived": false,
| "creator": {
| "id": 4374599,
| "username": "name xxxxxxx",
| "color": "#006063",
| "email": "xxxx@xxx.com",
| "profilePicture": "https://xxxxxxxx.jpg"
| },
| "assignees": [
| {
| "id": 000000,
| "username": "xxxxx",
| "color": "#81b1ff",
| "initials": "xx",
| "email": "xxxxx@xxxxxxx.com",
| "profilePicture": "https://xxxxxxx.jpg"
| }
| ],
| "watchers": [
|
| ],
| "checklists": [
|
| ],
| "tags": [
|
| ],
| "parent": null,
| "priority": null,
| "due_date": null,
| "start_date": null,
| "points": null,
| "time_estimate": null,
|
| "custom_fields": [
| {
| "id": "d0c016df-e09a-492e-a7a2-cc92e1993627",
| "name": "Sprint",
| "type": "labels",
| "type_config": {
| "options": [
| {
| "id": "64e19188-8440-4b35-8459-d49348c92e55",
| "label": "2021w14",
| "color": "#e50000"
| },
| {
| "id": "f71958f7-1c87-484e-8857-c2f90ef80f69",
| "label": "2021w13",
| "color": "#2ecd6f"
| },
| {
| "id": "a7f835c8-76fe-42e9-85a9-50a8af8dd6ea",
| "label": "2021w12",
| "color": "#fff"
| },
| {
| "id": "aaf2fcc6-ec8d-4eb8-8621-cac2c8063f17",
| "label": "2021w11",
| "color": "#fff"
| }
| ]
| },
| "date_created": "xxxxxxxxxxxx",
| "hide_from_guests": false,
| "value": [
| "64e19188-8440-4b35-8459-d49348c92e55"
| ],
| "required": false
| },
| {
| "id": "4513e77f-9866-4139-bcc5-458945db0deb",
| "name": "Points",
| "type": "drop_down",
| "type_config": {
| "new_drop_down": true,
| "options": [
| {
| "id": "02b60555-6814-4bfa-9a4a-3c5728261f9b",
| "name": "1",
| "color": null,
| "orderindex": 0
| },
| {
| "id": "d7fce482-c1d2-4732-a6f9-4268c447c4b7",
| "name": "2",
| "color": null,
| "orderindex": 1
| },
| {
| "id": "b7b99b87-7639-4286-8e6f-96966718ff1f",
| "name": "3",
| "color": null,
| "orderindex": 2
| }
| ]
| },
| "date_created": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
| "hide_from_guests": false,
| "required": false
| },
| {
| "id": "xxxxxx_xxxxxx_xxxxxxx_xxxxx",
| "name": "Progreso",
| "type": "xxxxxxxxxxxxxxxxx",
| "type_config": {
| "tracking": {
| "checklists": true,
| "assigned_comments": true
| },
| "complete_on": 3
| },
| "date_created": "xxxxxxxxxxxxxxxxxxxxx",
| "hide_from_guests": false,
| "value": {
| "percent_complete": 100
| },
| "required": null
| }
| ],
|
| "dependencies": [
|
| ],
| "linked_tasks": [
|
| ],
| "team_id": "xxxxxxxx",
| "url": "xxxxxxxxxx",
| "permission_level": "create",
| "list": {
| "id": "xxxxxxxxx",
| "name": "xxxxxxx 02-06",
| "access": true
| },
| "project": {
| "id": "xxxxxxxxx",
| "name": "Sprints",
| "hidden": false,
| "access": true
| },
| "folder": {
| "id": "xxxxxxxxx",
| "name": "Sprints",
| "hidden": false,
| "access": true
| },
| "space": {
| "id": "4286586"
| }
|}
| ]
| }
|""".stripMargin
object codecs {
import auto._
implicit val movieEncoder: Encoder[TasksItem] = deriveEncoder[TasksItem]
implicit val movieDecoder: Decoder[TasksItem] = deriveDecoder[TasksItem]
}
private object auto extends AutoDerivation {
import shapeless._
// list value
implicit def encoderValueClass[T <: AnyVal, V](implicit
g: Lazy[Generic.Aux[T, V :: HNil]],
e: Encoder[V]
): Encoder[T] = Encoder.instance { element ⇒
e(g.value.to(element).head)
}
implicit def decoderValueClass[T <: AnyVal, V](implicit
g: Lazy[Generic.Aux[T, V :: HNil]],
d: Decoder[V]
): Decoder[T] = Decoder.instance { cursor ⇒
d(cursor).map { element ⇒
g.value.from(element :: HNil)
}
}
implicit val configuration: Configuration = Configuration.default.withDiscriminator("type")
}
import codecs._
val decoded:Either[circe.Error, TasksItem] = circe.jawn.decode[TasksItem](json)
println(decoded)
}
案例类代码:
case class ValueSprint(value:Option[List[String]]=None ) extends AnyVal
case class ValueStoryPoint(value:Option[Int]=None ) extends AnyVal
case class ValueStoryProgreso(value:Option[Int]=None ) extends AnyVal
case class ValueItem( percent_complete:ValueStoryProgreso )
sealed trait ElementTask
case class CustomFieldsItemSprint( id:Option[String]=None, name:Option[String]=None, `type`:Option[String]=None,
date_created:Option[String]=None,
hide_from_guests:Option[Boolean]=None, value:ValueSprint ,
required:Option[Boolean]=None ) extends ElementTask
case class CustomFieldsItemStoryPoint ( id:Option[String]=None, name:Option[String]=None, `type`:Option[String]=None,
date_created:Option[String]=None,
hide_from_guests:Option[Boolean]=None, value: ValueStoryPoint ,
required:Option[Boolean]=None ) extends ElementTask
case class CustomFieldsItemProgreso ( id:Option[String]=None, name:Option[String]=None, `type`:Option[String]=None,
date_created:Option[String]=None,
hide_from_guests:Option[Boolean]=None, value:Option[ValueItem]=None ,
required:Option[Boolean]=None ) extends ElementTask
case class SpaceItem(id:Option[String]=None)
case class FolderItem( id: Option[String]=None, name:Option[String]=None, hidden:Option[Boolean]=None, access:Option[Boolean]=None)
case class ProjectItem( id: Option[String]=None, name:Option[String]=None, hidden:Option[Boolean]=None, access:Option[Boolean]=None)
case class ListItem( id:Option[String]=None, name:Option[String]=None, access:Option[Boolean]=None )
case class TagsItem (name:Option[String]=None, tag_fg: Option[String]=None, tag_bg:Option[String]=None, creator: Option[Long]=None)
case class AssigneesItem( id:Option[Long]=None, username:Option[String]=None, color:Option[String]=None, initials:Option[String]=None,
email: Option[String]=None, profilePicture: Option[String]=None)
case class CreatorItem( id:Option[Long]=None, username:Option[String]=None, color:Option[String]=None, email:Option[String]=None,
profilePicture:Option[String]=None )
case class StatusItem( status:Option[String]=None, color:Option[String]=None, `type`:Option[String]=None, orderindex:Option[Int]=None)
case class DependenciesItem( task_id:Option[String]=None, depends_on:Option[String]=None, `type`:Option[Int],
date_created:Option[String]=None, userid:Option[String]=None)
case class PriorityItem( id:Option[String]=None, priority:Option[String]=None, color:Option[String]=None, orderindex:Option[String]=None)
case class OptionsItem(id:Option[String]=None, label:Option[String]=None, name:Option[String]=None, color:Option[String]=None, orderindex:Option[Int]=None )
case class TrackingItem(checklists:Option[Boolean], assigned_comments:Option[Boolean])
case class TypeConfigItem( new_drop_down:Option[Boolean]=None, options:Option[List[OptionsItem]]=None , complete_on:Option[Int], tracking:Option[TrackingItem] )
case class ChecklistsItem( id:Option[String]=None, task_id:Option[String]=None, name:Option[String]=None, date_created:Option[String]=None, orderindex:Option[Int]=None, creator:Option[Long]=None, resolved:Option[Int]=None, unresolved:Option[Int]=None )
case class TaskItem ( id:Option[String]=None, custom_id:Option[String]=None , name:Option[String]=None, text_content:Option[String]=None,
description:Option[String]=None, status:Option[StatusItem]=None, orderindex:Option[String]=None, date_created:Option[String]=None ,
date_updated: Option[String]=None , date_closed:Option[String]=None, archived:Option[Boolean]=None,
creator:Option[CreatorItem] , assignees:Option[List[AssigneesItem]]=None , watchers:Option[List[String]]=None ,
checklists:Option[List[ChecklistsItem]]=None , tags:Option[List[TagsItem]]=None , parent:Option[String]=None ,
priority:Option[PriorityItem]=None , due_date:Option[String]=None , start_date:Option[String]=None ,
points:Option[String]=None , time_estimate:Option[Long]=None , time_spent:Option[Long]=None ,
custom_fields:Option[List[ElementTask] ]=None ,
dependencies:Option[List[DependenciesItem]]=None, linked_tasks:Option[List[String]]=None ,
team_id:Option[String]=None , url:Option[String]=None , permission_level:Option[String]=None,
list:Option[ListItem]=None , project:Option[ProjectItem]=None , folder:Option[FolderItem]=None ,
space:Option[SpaceItem]=None )
case class TasksItem ( tasks:Option[List[TaskItem]]=None ) // main case class
在此字段上失败: custom_fields:Option[List[ElementTask] ]=None ,
我需要修改什么或缺少什么才能使其正常工作?
解决方案
推荐阅读
- vba - xlPastevalues 给了我一个对象定义的错误 VBA
- java - “受保护”修饰符没用吗?
- python - 如何使用循环用相关值填充 3D 矩阵?
- floating-point - 将浮点数转换为包含所有数字的字符串
- webpack - 如何在 DOM 准备好之前开始动态加载 JS?
- asp.net-mvc - AspNetZero - .NET Core 2.0 MVC 版本 - 添加新控制器时出错
- javascript - 如何将对象字符串拆分为单个字符串?VueJS 和计算属性
- xamarin - Visual Studio 2017、Xamarin 和更新包
- r - 基于其他变量创建新变量,并在 R 中循环变量名称
- html - Bootstrap 4列对齐问题