Skip to content

Instantly share code, notes, and snippets.

@lloyd
Created May 26, 2011 19:52
Show Gist options
  • Save lloyd/993928 to your computer and use it in GitHub Desktop.
Save lloyd/993928 to your computer and use it in GitHub Desktop.

This perhaps deserves a blog post, but whatever.

The challenge: What if you wanted to determine what level my Bulgarian language skills were at? Using this sample document:

{
    "name": {
        "first": "Lloyd",
        "last": "Hilaiel"
    },
    "favoriteColor": "yellow",
    "languagesSpoken": [
        {
            "language": "Bulgarian",
            "level": "advanced"
        },
        {
            "language": "English",
            "level": "native"
        },
        {
            "language": "Spanish",
            "level": "beginner"
        }
    ],
    "seatingPreference": [
        "window",
        "aisle"
    ],
    "drinkPreference": [
        "whiskey",
        "beer",
        "wine"
    ],
    "weight": 172
}

How would you craft a CSS-like selector to extract the value of the key level contained in an object where language has a value of Bulgarian? (i.e. match the string advanced)

NOW STOP. Don't read any more of this gist. Go think about it. Do you know any CSS that should apply here?

By this time, you've already thought of your own answer. A few possible (sometimes complimentary) routes are in my head.

The first solution is general and has precedent in jquery/sizzle. Specifically :has() and :contains() inspired by jquery. But maybe we'd rename :contains to :val.

.languagesSpoken :has(.language:val('Bulgarian')) .level

The other route could repurpose attribute selectors to say that they match children

.languagesSpoken object[language="Bulgarian"] .level

Note that the former is potentially much more flexible because the :has() pseudo function can take complete selectors.

Note also that the latter could be a syntactic abbreviation for the former. This is in fact how things like :first-child and :last-child are implemented in the reference implementation.

Another option could use the sibling combinator from css3:

.languagesSpoken .language:val('Bulgarian') ~ .level
@fd
Copy link

fd commented May 27, 2011

Note that the former is potentially much more flexible because the :has() pseudo function can take complete selectors.

Note also that the latter could be a syntactic abbreviation for the former. This is in fact how things like :first-child and :last-child are implemented in the reference implementation.

I think you're spot-on about :has being more flexible.

@lloyd
Copy link
Author

lloyd commented May 27, 2011

:has() has been promoted from maybe to is status in the docs: http://jsonselect.org/#docs/overview

@fd
Copy link

fd commented May 27, 2011 via email

@lloyd
Copy link
Author

lloyd commented May 27, 2011

I'll try to include tests and close this issue today: lloyd/JSONSelect#16

@fd
Copy link

fd commented May 27, 2011 via email

@neonux
Copy link

neonux commented May 27, 2011

The latter could be more abbreviated even, exactly like in CSS :

.languagesSpoken [language="Bulgarian"] .level

You can also handle multiple checks like in CSS :

.languagesSpoken [language='Bulgarian'][native='yes'][present] .level

How to handle this with :has ?

.languagesSpoken :has(.language:val('Bulgarian')):has(.native:val('yes')):has('present') .level 

?

I like the potential flexibility of :has with inner selectors but only having this one to check properties would imho make JSONSelect unnecessarily more verbose, and 'diverge' from CSS, for the likely most common use cases (property presence check, property value check and other simple CSS attribute selectors).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment