Git Product home page Git Product logo

Comments (10)

whitlockjc avatar whitlockjc commented on June 14, 2024

Being open source, I'll take a peek and see if there is a PR I can craft up. But in the meantime, I'd love any sort of discussion around this.

from jsonpath.

whitlockjc avatar whitlockjc commented on June 14, 2024

I'm starting to think this could be a feature request for this repo or https://github.com/PaesslerAG/gVal because I'm not sure there is a way to parse the JSONPath (complex enough to avoid doing at all/most costs) and use the placeholder hack to generate full paths for all resolved values. I think for simple cases (direct value resolution, single-segment wildcards, single-levels of recursion) it could be possible but more complex cases (recursion with segments after recursion, any level of nested recursion) fails based on my understanding. I think there are other cases that I remember being difficult/impossible but that's the gist of it.

I'll keep digging but as of right now, this is definitely not straight forward.

from jsonpath.

whitlockjc avatar whitlockjc commented on June 14, 2024

To shed some light on this based on a slightly convoluted example (modified from https://jsonpath.com), here is an example JSON document and the example placeholders returned for a few JSONPath expressions:

JSON

{
  "firstName": "John",
  "lastName": "doe",
  "age": 26,
  "address": {
    "streetAddress": "naist street",
    "city": "Nara",
    "postalCode": "630-0192"
  },
  "phoneNumbers": [
    {
      "type": "iPhone",
      "number": "0123-4567-8888"
    },
    {
      "type": "home",
      "number": "0123-4567-8910"
    },
    {
      "miscellaneous": [
        {
          "type": "mobile",
          "number": "0123-4567-8910",
          "nested": [
            {
              "type": "work",
              "number": "0123-4567-8910"
            }
          ]
        }
      ]
    }
  ],
  "others": [
    {
      "number": {
        "details": {
          "type": "home",
          "content": "0123-4567-8910"
        }
      }
    },
    {
      "number": {
        "details": {
          "type": "work",
          "content": "0123-4567-8910"
        }
      }
    }
  ],
  "nested": {
    "others": [
      {
        "number": {
          "details": {
            "type": "home",
            "content": "0123-4567-8910"
          }
        }
      },
      {
        "number": {
          "details": {
            "type": "work",
            "content": "0123-4567-8910"
          }
        }
      }
    ]
  }
}

Results

jsonpath.Get($)
  * $
jsonpath.Get($..number)
  * $["others"]["1"]
  * $["phoneNumbers"]["0"]
  * $["phoneNumbers"]["1"]
  * $["phoneNumbers"]["2"]["miscellaneous"]["0"]
  * $["phoneNumbers"]["2"]["miscellaneous"]["0"]["nested"]["0"]
  * $["nested"]["others"]["0"]
  * $["nested"]["others"]["1"]
  * $["others"]["0"]
jsonpath.Get($.phoneNumbers[?(@.type == "home")].number)
  * $["1"]
jsonpath.Get($..phoneNumbers..number)
  * $["0"]
  * $["1"]
  * $["2"]["miscellaneous"]["0"]
  * $["2"]["miscellaneous"]["0"]["nested"]["0"]
jsonpath.Get($.phoneNumbers..number)
  * $["0"]
  * $["1"]
  * $["2"]["miscellaneous"]["0"]
  * $["2"]["miscellaneous"]["0"]["nested"]["0"]
jsonpath.Get($.phoneNumbers[*].number)
  * $["0"]
  * $["1"]
jsonpath.Get($.others..details.type)
  * $["1"]["number"]
  * $["0"]["number"]
jsonpath.Get($..number..type)
  * $["nested"]["others"]["0"]["details"]
  * $["nested"]["others"]["1"]["details"]
  * $["others"]["0"]["details"]
  * $["others"]["1"]["details"]

Based on these examples (convoluted, I know...but I'm trying to identify edge cases), assuming I could correctly parse the JSONPath, I could reconstruct the actual path for all non-multi-nested expressions. But for multi-nested expressions (Example: $..number..type), I can't tell which segments belong to the first .. and which belong to the second ...

from jsonpath.

whitlockjc avatar whitlockjc commented on June 14, 2024

Actually, https://github.com/PaesslerAG/jsonpath/blob/master/jsonpath.go#L7 may have just given me a possibility, but if I understand things, it won't be ideal due to the fact you'd have to evaluate the JSONPath expression N times (where N is the number of wildcards).

from jsonpath.

whitlockjc avatar whitlockjc commented on June 14, 2024

After a good deal of mucking around, it seems like the context between the JSONPath parsing and the gVal execution is different, so attempting to update parse.go and placeholder.go to work in uniform isn't possible from what I can tell. I can get parse.go to keep track of the path segments it knows of and I can get placeholder.go to keep track of the wildcard path segments, but tying them together appropriately hasn't worked (yet).

If anyone more knowledgeable on gVal and/or jsonpath can share some ideas, let me know. I'm going to punt for a bit, I've been stuck on this for a few days and I need to make progress elsewhere. @generikvault maybe?

from jsonpath.

whitlockjc avatar whitlockjc commented on June 14, 2024

Okay, I just found out about gval.NewEvaluableWithContext...gonna give this a shot.

from jsonpath.

whitlockjc avatar whitlockjc commented on June 14, 2024

Using gval.NewEvaluableWithContext doesn't seem to yield any success, the disconnect is still there.

from jsonpath.

whitlockjc avatar whitlockjc commented on June 14, 2024

What if we just created a new API like GetWithPaths that works like the placeholder hack but it returns a map of full paths to resolved values? I'll look into this, but figuring out the best way to keep track of path segments for nested selectors has been painful thus far.

from jsonpath.

whitlockjc avatar whitlockjc commented on June 14, 2024

Okay, I will have a PR together that should handle this and it does not use the "placeholder hack". While I would also consider this to be a hack of sorts, it works and instead of hoping to share a common scope to keep track of these automatically, I resort to a much simpler approach. Here is the process:

  1. At parse time, turn the provided JSONPath into a more sanitized path replacing ambiguous selectors with *. (For example, $.phoneNumbers[*].number would become ["phoneNumbers", "*", "number"], and $..number would become ["*", "number"].)
  2. parse.go was updated with the following:
    a. parser was updated to have a sanitizedPath set at parse time
    b. .parseRootPath sets a context variable (computePathsContextKey{}) to true
    c. .parseCurrentPath sets a context variable (computePathsContextKey{}) to false
    d. parser.parse was updated to return a closure that checks the value of the aforementioned context variable and returns either the raw value (parseCurrentPath) or a map whose keys are the full JSONPath to the matched value and whose values are the matched value.
  3. jsonpath.go was updated to have a new GetWithPaths function that will return a map (key is the quoted JSONPath similar to the placeholder approach, value is the resolved value) (thanks to a helper method that makes it where Get and GetWithPaths work appropriately).

For the example above, here are the new keys (matched values are identical to Get prior to these changes):

jsonpath.Get($)
  $
jsonpath.Get($.phoneNumbers[0].type)
  $["phoneNumbers"]["0"]["type"]
jsonpath.Get($.phoneNumbers..nested[0].number)
  $["phoneNumbers"]["2"]["miscellaneous"]["0"]["nested"]["0"]["number"]
jsonpath.Get($.phoneNumbers[*])
  $["phoneNumbers"]["0"]
  $["phoneNumbers"]["1"]
  $["phoneNumbers"]["2"]
jsonpath.Get($.phoneNumbers)
  $["phoneNumbers"]
jsonpath.Get($..number)
  $["nested"]["others"]["1"]["number"]
  $["others"]["0"]["number"]
  $["others"]["1"]["number"]
  $["phoneNumbers"]["0"]["number"]
  $["phoneNumbers"]["1"]["number"]
  $["phoneNumbers"]["2"]["miscellaneous"]["0"]["number"]
  $["phoneNumbers"]["2"]["miscellaneous"]["0"]["nested"]["0"]["number"]
  $["nested"]["others"]["0"]["number"]
jsonpath.Get($.phoneNumbers[?(@.type == "home")].number)
  $["phoneNumbers"]["1"]["number"]
jsonpath.Get($..phoneNumbers..number)
  $["phoneNumbers"]["2"]["miscellaneous"]["0"]["nested"]["0"]["number"]
  $["phoneNumbers"]["0"]["number"]
  $["phoneNumbers"]["1"]["number"]
  $["phoneNumbers"]["2"]["miscellaneous"]["0"]["number"]
jsonpath.Get($.phoneNumbers..number)
  $["phoneNumbers"]["0"]["number"]
  $["phoneNumbers"]["1"]["number"]
  $["phoneNumbers"]["2"]["miscellaneous"]["0"]["number"]
  $["phoneNumbers"]["2"]["miscellaneous"]["0"]["nested"]["0"]["number"]
jsonpath.Get($.phoneNumbers[*].number)
  $["phoneNumbers"]["0"]["number"]
  $["phoneNumbers"]["1"]["number"]
jsonpath.Get($.others..details.type)
  $["others"]["0"]["number"]["details"]["type"]
  $["others"]["1"]["number"]["details"]["type"]
jsonpath.Get($.others..details["type"])
  $["others"]["0"]["number"]["details"]["type"]
  $["others"]["1"]["number"]["details"]["type"]
jsonpath.Get($..number..type)
  $["others"]["1"]["number"]["details"]["type"]
  $["nested"]["others"]["0"]["number"]["details"]["type"]
  $["nested"]["others"]["1"]["number"]["details"]["type"]
  $["others"]["0"]["number"]["details"]["type"]

from jsonpath.

whitlockjc avatar whitlockjc commented on June 14, 2024

I ended up with a much simpler approach, although sanitizing the paths returned by ambiguousPath required a little extra work to figure out. All-in-all, I think PR #44 is in decent shape. Let me know what your thoughts are.

from jsonpath.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.