深度删除circe scala中特定的空json数组

发布于 2025-01-20 16:01:04 字数 1734 浏览 3 评论 0原文

我想在 circe 处理之前/期间从 json 中删除深层空 json 数组。

传入的 JSON

{
    "config": {
        "newFiles": [{
            "type": "audio",
            "value": "welcome1.mp3"
        }],
        "oldFiles": [],
        "channel": "BC"
    }
}

or 

{
    "config": {
        "newFiles": [],
        "oldFiles": [{
            "type": "audio",
            "value": "welcome1.mp3"
        }],
        "channel": "BC"
    }
}

结果 Json 应该看起来像

{
    "config": {
        "newFiles": [{
            "type": "audio",
            "value": "welcome1.mp3"
        }],
        "channel": "BC"
    }
}

or 

{
    "config": {
        "oldFiles": [{
            "type": "audio",
            "value": "welcome1.mp3"
        }],
        "channel": "BC"
    }
}

据我所知,这可以在解码配置之前以及解码配置期间完成。 这里的想法是我只想在我的案例类级别处理一个文件(新的或旧的)。

方法1:在配置解码级别尝试过,效果很好。

case class File(`type`: String, value: String)

case class Config(files: List[File],
                  channel: String = "BC")

object Config{
  implicit final val FileDecoder: Decoder[File] = deriveDecoder[File]
  implicit val ConfigDecoder: Decoder[Config] = (h:HCursor) =>
    for {
      oldFiles <- h.get[List[File]]("oldFiles")
      files <- if (oldFiles.isEmpty) h.get[List[File]]("newFiles") else h.get[List[File]]("oldFiles")
      channel <- h.downField("channel").as[String]
    }yield{
      Config(files, channel)
    }
}

case class Inventory(config: Config)

object Inventory {
  implicit val InventoryDecoder: Decoder[Inventory] = deriveDecoder[Inventory]
}

方法2:在输入解码之前尝试过,但没有成功

让我知道处理它的优雅方法是什么。 PS:我有多个类似的配置解码器,如果我在配置解码级别处理这个问题,那么将会有很多样板代码。

I want to remove deep empty json array from my json before/during processing by circe.

Incoming JSON

{
    "config": {
        "newFiles": [{
            "type": "audio",
            "value": "welcome1.mp3"
        }],
        "oldFiles": [],
        "channel": "BC"
    }
}

or 

{
    "config": {
        "newFiles": [],
        "oldFiles": [{
            "type": "audio",
            "value": "welcome1.mp3"
        }],
        "channel": "BC"
    }
}

Resulted Json should look like

{
    "config": {
        "newFiles": [{
            "type": "audio",
            "value": "welcome1.mp3"
        }],
        "channel": "BC"
    }
}

or 

{
    "config": {
        "oldFiles": [{
            "type": "audio",
            "value": "welcome1.mp3"
        }],
        "channel": "BC"
    }
}

What i understand that this can be done before decoding config as well as during decoding config.
The idea here is i want to handle only one of files (either new or old) at my case class level.

Method 1: Tried at config decoding level which works well.

case class File(`type`: String, value: String)

case class Config(files: List[File],
                  channel: String = "BC")

object Config{
  implicit final val FileDecoder: Decoder[File] = deriveDecoder[File]
  implicit val ConfigDecoder: Decoder[Config] = (h:HCursor) =>
    for {
      oldFiles <- h.get[List[File]]("oldFiles")
      files <- if (oldFiles.isEmpty) h.get[List[File]]("newFiles") else h.get[List[File]]("oldFiles")
      channel <- h.downField("channel").as[String]
    }yield{
      Config(files, channel)
    }
}

case class Inventory(config: Config)

object Inventory {
  implicit val InventoryDecoder: Decoder[Inventory] = deriveDecoder[Inventory]
}

Method 2: Tried before feeding into decoding which didn't worked out

Let me know what could be the elegant approach to handle it.
PS: I have multiple similar config decoders and if i handle this at config decoding level then there will be a lot of boiler plate code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

陈独秀 2025-01-27 16:01:04

我再次简化了问题,但是将其与先前的答案结合起来应该很简单。

我还利用cats.data.nonementylist

final case class Config(files: NonEmptyList[String], channel: String = "BC")
object Config {
  implicit final val ConfigDecoder: Decoder[Config] =
    (
      (Decoder[NonEmptyList[String]].at(field = "newFiles") or Decoder[NonEmptyList[String]].at(field = "oldFiles")),
      Decoder[String].at(field = "channel")
    ).mapN(Config.apply).at(field = "config")
}

可以这样使用:

val data =
"""[
{
"config": {
  "newFiles": ["Foo", "Bar", "Baz"],
  "oldFiles": [],
  "channel": "BC"
}
},
{
"config": {
  "newFiles": [],
  "oldFiles": ["Quax"],
  "channel": "BC"
}
}
]"""

parser.decode[List[Config]](data)
// res: Either[io.circe.Error, List[Config]] =
//   Right(List(
//     Config(NonEmptyList("Foo", "Bar", "Baz"), "BC"),
//     Config(NonEmptyList("Quax"), "BC")
//   ))

注意:我假设两个列表中的至少一个必须是非空的,并优先考虑新列表。


您可以看到运行的代码在这里

I have simplified the problem a little bit again, but combining this with the previous answer should be simple.

I also took advantage of cats.data.NonEmptyList

final case class Config(files: NonEmptyList[String], channel: String = "BC")
object Config {
  implicit final val ConfigDecoder: Decoder[Config] =
    (
      (Decoder[NonEmptyList[String]].at(field = "newFiles") or Decoder[NonEmptyList[String]].at(field = "oldFiles")),
      Decoder[String].at(field = "channel")
    ).mapN(Config.apply).at(field = "config")
}

This can be used like this:

val data =
"""[
{
"config": {
  "newFiles": ["Foo", "Bar", "Baz"],
  "oldFiles": [],
  "channel": "BC"
}
},
{
"config": {
  "newFiles": [],
  "oldFiles": ["Quax"],
  "channel": "BC"
}
}
]"""

parser.decode[List[Config]](data)
// res: Either[io.circe.Error, List[Config]] =
//   Right(List(
//     Config(NonEmptyList("Foo", "Bar", "Baz"), "BC"),
//     Config(NonEmptyList("Quax"), "BC")
//   ))

Note: I am assuming that at least one of the two lists must be non-empty and give priority to the new one.


You can see the code running here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文