https://kotlinlang.org logo
#arrow
Title
# arrow
d

dave08

03/19/2023, 11:53 AM
Hi! It seems like if I have
{ "some": "json" }
and I have
JsonPath["some2"].set(json, JsonPrimitive("json2"))
it just silently doesn't do anything... I would have expected it to create the new key. Is there any way to do that?
Copy code
val json = buildJsonObject {
    put("some", buildJsonArray {
        add("this")
        add("that")
    })
}

val path = JsonPath["some2"].array

println(path.set(json, listOf(JsonPrimitive("one"), JsonPrimitive("two"))))
println(path.modify(json) { listOf(JsonPrimitive("one"), JsonPrimitive("two")) })
Gives:
Copy code
{"some":["this","that"]}
{"some":["this","that"]}
@simon.vergauwen
s

simon.vergauwen

03/19/2023, 2:10 PM
Yes, you cannot set a key that doesn't exist. At least not with
Optional
. It's possible however with
Lens<JsonElement, JsonArray?>
.
d

dave08

03/19/2023, 2:11 PM
I'm REAALLLY struggling with this... since JsonPath.at() which seems to be the only way to set an unexistant key isn't composable with my original Prism...
And using JsonPath[] with a Prism doesn't seem to be settable.
Copy code
val prism2: Prism<JsonElement, List<FooValueClass>?> = Prism(
    getOption = { orig ->
            if (orig !is JsonArray) return@Prism none()
        
            orig.mapNotNull { it.jsonPrimitive.contentOrNull?.let { value -> FooValueClass(value) } }
                .takeIf { orig.size == it.size }?.some() ?: none()
    },
    reverseGet = {
        if (it == null) return@Prism JsonNull
        
        JsonArray(it.map { value -> JsonPrimitive(value.value) }) 
    }
)

// This doesn't work... there's no such compose function...
val setter = JsonPath["some2"] compose prism2
Am I doing something wrong @simon.vergauwen?
Even this doesn't work...
Copy code
val lens1 = Lens<List<JsonElement>, List<JsonElement>, List<PackageName>?, List<PackageName>?>(
    get = { orig: List<JsonElement> ->
        orig.mapNotNull {
            it.jsonPrimitive.contentOrNull?.let { value -> PackageName(value) }
        }
        .takeIf { orig.size == it.size } },
    set = { orig: List<JsonElement>, curr: List<PackageName>? ->
        curr?.map { value -> JsonPrimitive(value.value) } as List<JsonElement>
    }
)

val setter = JsonPath["some2"].array compose lens1
JsonPath[] returns
Optional<JsonElement, JsonElement>
so even if I compose it with that Lens, it won't be settable?
And
at()
that seems to allow setting an unexistant value isn't composable while keeping that quality of being able to set the value...
I finally got it (after a lot of toil and trouble... 😵‍💫😞
Copy code
val lens2 = Lens<Option<JsonElement>, Option<JsonElement>, Option<List<PackageName>>, Option<List<PackageName>>>(
    get = { orig1: Option<JsonElement> ->
        orig1.flatMap { orig ->
            if (orig !is JsonArray) return@Lens none()
            orig.mapNotNull {
                it.jsonPrimitive.contentOrNull?.let { value -> PackageName(value) }
            }
                .takeIf { orig.size == it.size }?.some() ?: none()
        }
         
    },
    set = { orig: Option<JsonElement>, curr: Option<List<PackageName>> ->
        curr.flatMap { JsonArray(it.map { value -> JsonPrimitive(value.value) }).some() }
    }
)

val setter = <http://JsonPath.at|JsonPath.at>("some2") compose lens2
But isn't that inconsistent behaviour? I mean
JsonPath["..."]
is sooo different from
<http://JsonPath.at|JsonPath.at>("...")
?
s

simon.vergauwen

03/19/2023, 7:42 PM
It's not inconsistent,
select
or
[]
attempts to focus on a given key so
Optional
.
At
points to a specific key with a
Lens
. So it works exactly as I mentioned above.
At
returns
Lens<S, Option<A>>
while
select/[]
returns
Optional<S, A>
. https://arrow-kt.io/docs/optics/at/ It's two completely different operations.
2 Views