json-ld / json-ld.org

JSON for Linked Data's documentation and playground site
https://json-ld.org/
Other
852 stars 151 forks source link

Recursive index map of index maps #826

Open grenik opened 8 months ago

grenik commented 8 months ago

Is there a way to model map of maps?

I see only single-level maps: Property-based data indexing

But maps often have 2+ levels of indexing. Simplified example: animals are indexed by the size, and then by the color.

{
  "@context": {
    "schema": "http://schema.org/",
    "map": {
      "@id": "schema:animals",
      "@type": "@id",
      "@container": "@index",
      "@index": ["schema:size", "schema:color"]
    },
  },
  "map": {
    "small": {
      "green": "schema:Frog",
      "black": "schema:Ant"
    },
    "medium": {
      "black": "schema:Raven"
    },
    "big": {
      "green": "schema:Crocodile"
    }
  }
}

expansion result:

[
  {
    "http://schema.org/animals": [
      {
        "@id": "http://schema.org/Frog",
        "http://schema.org/size": [{"@value": "small"}],
        "http://schema.org/color": [{"@value": "green"}]
      },
      {
        "@id": "http://schema.org/Ant",
        "http://schema.org/size": [{"@value": "small"}],
        "http://schema.org/color": [{"@value": "black"}]
      },
      {
        "@id": "http://schema.org/Raven",
        "http://schema.org/size": [{"@value": "medium"}],
        "http://schema.org/color": [{"@value": "black"}]
      },
      {
        "@id": "http://schema.org/Crocodile",
        "http://schema.org/size": [{"@value": "big"}],
        "http://schema.org/color": [{"@value": "green"}]
      }
    ]
  }
]
gkellogg commented 8 months ago

Without going into your examples too far, you might look at Nested Properties which an allow nesting at arbitrary depth.

grenik commented 8 months ago

Nested properties require that I know their names and define them explicitly in the @context, but in my case I receive JSONs with arbitrary properties: an indexed maps nested inside another indexed map (callbacks in my example are first indexed by the callback name and then by a path). I need to convert property names (the keys used to index objects) to RDF property values.

I can achieve it using Property-based data indexing via @container: @indexand @index: URI, but only for one level, while the data I receive often contain multi-level maps, as shown in my examples.

Specification can be extended to model my case by allowing array values for @index:

{
  "@context": {
    "mapWith2LevelsAndSemanticKeys": {
      "@id": "...",
      "@container": "@index",
      "@index": ["schema:firstLevelKey", "schema:secondLevelKey"] // this already tells JSON-LD processor that map has 2 levels
    },
  }
}

If keys at certain levels can be ignored (have no semantic meaning), this could also be modelled by placing nulls in @index.

{
  "@context": {
    "mapWithFourLevels": {
      "@id": "...",
      "@container": "@index",
      "@index": [null, "schema:secondLevelKey", null, "schema:fourthLevelKey"] // skip keys on level 1 and 3
    },
  }
}
grenik commented 8 months ago

Real-life example:

For instance, callbacks in OpenAPI are first indexed by the callback name and then by the path (see spec, example)

Here is a simplified example of an API description with a single-level map pahts and a 2-level map callbacks:

{
  "paths": {
    "/registration": {
      "post": "registers user",
      "delete": "deletes account"
    },
    "/login": {
      "post": "login",
      "delete": "logout"
    }
  },
  "callbacks": {
    "Registration Callback": {
      "/onRegistration": {
        "post": "called when registered",
        "delete": "called when account is deleted"
      }
    },
    "Login Callback": {
      "/onLogin": {
        "post": "when logged in"
      },
      "/onLogout": {
        "post": "when logged out"
      }
    }
  }
}
BigBlueHat commented 7 months ago

@grenik sadly at this point, I don't think JSON-LD can support that structure. You'd need to convert it into something with less "implied"/structural semantics.

@gkellogg this is certainly a common case in human-facing JSON (well...YAML...) formats like OpenAPI, so I do wonder what it would take to support it. Everything I can imagine right now looks rather odd in the context definition, but maybe there's a way we could explore for doing multiple "stacked" indexes somehow. Something fun to explore, anyhow. 😀