cbeust / klaxon

A JSON parser for Kotlin
Apache License 2.0
1.85k stars 121 forks source link

Support path expressions in object binding @Json annotations #92

Open CapnSpellcheck opened 6 years ago

CapnSpellcheck commented 6 years ago

Would be nice to be able to do say: @Json(path="level1.level2.level3") for times when JSON has nested objects but you only want to construct a flat object from it.

cbeust commented 6 years ago

Oh... interesting.

To make sure I understand: even if the JSON document you parse has multiple nested objects in it, you can create one Kotlin object made of fields reaching arbitrarily deep into that JSON document?

I like the idea that your Kotlin hierarchy doesn't have to match JSON's. something that's occasionally a hassle when you parse JSON.

CapnSpellcheck commented 6 years ago

something that's occasionally a hassle when you parse JSON. Exactly!

cbeust commented 6 years ago

Actually you can already do this right now with a PathMatcher:

https://github.com/cbeust/klaxon/blob/master/src/test/kotlin/com/beust/klaxon/PathMatcherTest.kt#L12-L50

More verbose than your suggestion, though.

cbeust commented 6 years ago

I have the following working with very little changes right now:

    data class WithPath(
        val id: Int,
        @Json(path = "$.person.name")
        val name: String
    )

    fun fieldWithPath() {
        val result = Klaxon()
            .parse<WithPath>(StringReader("""{
            "id": 2,
            "person": {
               "name": "John"
            }
        }"""))
        assertThat(result).isEqualTo(WithPath(2, "John"))
    }

Trying to think of potential corner cases.

CapnSpellcheck commented 6 years ago

THe PathMatcher is not a bad approach. (I wasn't aware of it.) It will tie me over for awhile.

On Mon, Jan 22, 2018 at 1:56 PM, Cedric Beust notifications@github.com wrote:

I have the following working with very little changes right now:

data class WithPath(
    val id: Int,
    @Json(path = "$.person.name")
    val name: String
)

fun fieldWithPath() {
    val result = Klaxon()
        .parse<WithPath>(StringReader("""{
        "id": 2,
        "person": {
           "name": "John"
        }
    }"""))
    assertThat(result).isEqualTo(WithPath(2, "John"))
}

Trying to think of potential corner cases.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/cbeust/klaxon/issues/92#issuecomment-359545692, or mute the thread https://github.com/notifications/unsubscribe-auth/AAfQEIo9aLN40gusWEhEjlK7MegJMnP1ks5tNOgGgaJpZM4RmCuf .

cbeust commented 6 years ago

I have this implemented technically but from a feature standpoint, there are several areas I'm not sure how to handle.

For example, if you use a path in a class field, do you expect all the fields of that class to be populated via paths? If not, how do you handle mixed cases, where the objects are read via binding but occasionally, some of their fields come from some other part of the document?

Basically, paths don't mix well with object binding.

kairuyan commented 5 years ago

Hi. I have a question similar to the one above. for times when JSON has nested objects but you only want to construct a flat object from it. However, the last one is array. Add to The name of the json key is object. This is a reserved word in kotlin, so I gave it a name to replace it with another word, but it does not work.

@Json(path = "$.data.object.images[1]") var image : String @Json(path = "$.data.object.images") var images : JsonArray @Json(name = "object") var obj : JsonObject

Couldn't find path "$.data.object.images[0]" specified on field "image"

but @Json(paht = "$.data.object.name") var name : String get name value : tester

AntonTheDev commented 5 years ago

Does anyone know how to map a dictionary of dictionaries to a root level class instance?

My intent is to somehow map the following class

(class) PageConfig -> val analyticsPrefix : String? -> val pageTitle : String? -> var pressEvent : (class) AnalyticsEvent?

I have the following JSON .....

{
"analytics_prefix": "Group A",
"page_title": "Title",
"collection_view_analytics" :
  {
    "analytics_pressed_event" :
    {
      "analytics_label": "Price Pair Cell",
      "analytics_action": "Tapped",
      "analytics_page_name": "Market"
    },
    .....
  }
}

I've never run into such complexities on any platform other than Android trying to pick up Kotlin. As an iOS developer, there are so many mapping frameworks that have the flexibility to map nested dictionaries to an object, or through '.' separated nested keys, I cannot for the life of me find how to implement such rudimentary functionality. Even key words to search would help? What I'm noticing is that between Java, and Kotlin being new, there appears to be no clarity online through as to how one would achieve this.

cbeust commented 5 years ago

@AntonTheDev Please open a separate issue, or ask your question on the #klaxon Slack channel.

Thanks!

AntonTheDev commented 5 years ago

@cbeust Done :) https://github.com/cbeust/klaxon/issues/240

smac89 commented 4 years ago

It may be worth it to add full support for json-path