You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否像jq CLI工具一样用字符串JsonPath使用circe-optics并创建对应Lens?

Using String-Based JsonPath with circe-optics

Great question! Let's break down your two requirements and walk through practical, actionable solutions for each.

1. Mimicking jq-style String JsonPath with circe-optics

Circe doesn't include native support for string-based JsonPath queries directly in circe-optics, but you have two reliable ways to replicate this behavior:

Option A: Leverage circe-jackson for JsonPath parsing

Circe integrates with Jackson's mature JsonPath implementation via the circe-jackson module. You can use it to resolve a JsonPath string to a value, then pair it with optics if you need read/write capabilities. Here's a working example:

import io.circe._, io.circe.jackson.JsonPath
import monocle.Optional

// Sample JSON structure
val sampleJson = Json.obj(
  "field1" -> Json.obj(
    "field2" -> Json.arr(
      Json.obj("field3" -> Json.fromString("ignore")),
      Json.obj("field3" -> Json.fromString("target value"))
    )
  )
)

// Resolve value via JsonPath string
val pathResult: Option[Json] = JsonPath.query("$.field1.field2[1].field3")(sampleJson)

// Combine with optics for updates
pathResult.foreach { _ =>
  val updatedJson = JsonPath.update("$.field1.field2[1].field3", Json.fromString("new value"))(sampleJson)
  println(updatedJson.spaces2)
}

Option B: Build a custom string-to-optic parser

For tight integration with circe-optics, you can write a lightweight parser that converts a JsonPath-style string into the corresponding Optional (or Lens) step-by-step. This lets you use the full power of optics directly with string paths:

import io.circe.optics.JsonPath._
import monocle.Optional

def parseJsonPath(path: String): Optional[Json, Json] = {
  // Split path into segments, handling both fields and array indices
  val segments = path.split("\\.").flatMap { segment =>
    if (segment.contains("[")) {
      val field = segment.takeWhile(_ != '[')
      val index = segment.dropWhile(_ != '[').drop(1).dropRight(1).toInt
      List(Left(field), Right(index))
    } else {
      List(Left(segment))
    }
  }

  // Build the optic by folding through segments
  segments.foldLeft[Optional[Json, Json]](root) {
    case (current, Left(field)) => current.field(field)
    case (current, Right(index)) => current.index(index)
  }
}

// Usage example
val myOptic = parseJsonPath("field1.field2[1].field3")
val targetValue: Option[String] = myOptic.string.getOption(sampleJson)
val updatedJson: Json = myOptic.set(Json.fromString("updated value"))(sampleJson)

You can extend this parser to support more JsonPath features (like wildcards or filters) if your use case requires it.

2. Creating a Lens from a String Path

Absolutely! The parseJsonPath function above returns an Optional[Json, Json], which is the safest choice for Json paths (since any segment could be missing). If you know the path will always exist in your JSON structure, you can convert this Optional to a Lens using monocle.Lens.fromOptional with a default value:

import monocle.Lens

def parsePathToLens(path: String, fallbackValue: Json): Lens[Json, Json] = {
  val baseOptional = parseJsonPath(path)
  Lens.fromOptional(baseOptional)(_ => fallbackValue)
}

// Usage (assuming path exists)
val myLens = parsePathToLens("field1.field2[1].field3", Json.fromString("fallback"))
val currentValue: String = myLens.get(sampleJson).asString.get
val updatedJson: Json = myLens.set(Json.fromString("new lens value"))(sampleJson)

内容的提问来源于stack exchange,提问作者Bartek Andrzejczak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:09:08