Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Default content in term definition #426

Closed
gkellogg opened this issue Sep 20, 2016 · 5 comments
Closed

Proposal: Default content in term definition #426

gkellogg opened this issue Sep 20, 2016 · 5 comments

Comments

@gkellogg
Copy link
Member

There are a number of different issues regarding the need to define content for a term which is expanded in node values of that term, creating term-specific contexts, or supporting scoped contexts for framing. I think that all of these may be satisfied by specifying default content which is added to the node values of that term.

  • 247 Scoped Contexts – This feature seeks to allow @context to be used within a term definition, which would allow for additional term definitions to come into affect for values of that term.

  • 262 JSON key specific default namespaces for values – This proposal seeks to allow the definition of @vocab within a term definition, which would change the way simple keys and types are resolved for values of that term. (Clearly, this could be implemented using Scoped contexts #247).

  • 315 Framing should use embedded contexts for compaction – This proposal suggested allowing a frame to use different @context at different levels, which would be preserved in the framed document. If Scoped contexts #247 were supported, this would have the same effect.

  • 369 Term-level @base/@vocab – Pretty much the same as Feature proposal: JSON key specific default namespaces for values #262 and could be implemented by Scoped contexts #247.

  • 415 Scoping context based on the @type – describes scoping contexts based on @type. This is similar to Scoped contexts #247, but rather than basing it on node values of a term, it is automatically applied to node definitions having a specific @type. It's not clear what existing mechanism this might hook off of, and IMHO, this is better supported via Scoped contexts #247 (or similar).

  • We've also seen a general mis-understanding of how @context works, with the supposed capability to generate triples based on things in the @context (see Possible to generate implied triples from @context?, for one).

It strikes me that these could all be supported by a single mechanism to add default content to a node values of a term. Consider, for example:

{
  "@context": {
    "schema": "http://schema.org/",
    "foaf": "http://xmlns.com/foaf/0.1/",
    "bibo": "http://purl.org/ontology/bibo/",
    "@vocab": "schema",
    "author": {
      "@id": "schema:author",
      "@type": "@id",
      "@content": {
        "@context": {
          "@vocab": "foaf",
          "homepage": {"@id": "foaf:homepage", "@type": "@id"}
        },
        "@type": ["foaf:Person", "schema:Person"]
      }
    }
  },
  "@id": "http://www.w3.org/TR/json-ld/",
  "@type": "bibo:Document",
  "author": {
    "name": "Manu Sporny",
    "homepage": "http://manu.sporny.org/"
  }
}

The expansion rules for node values of term having @content would be to merge the value of @content into those definitions, meaning that keys and/or values are added as necessary, so that further expansion of that node definition would include those values. Note that as @context is part of @content, this would add that context to the node definition. This would effectively be equivalent to the following:

{
  "@context": {
    "schema": "http://schema.org/",
    "foaf": "http://xmlns.com/foaf/0.1/",
    "bibo": "http://purl.org/ontology/bibo/",
    "@vocab": "schema",
    "author": {
      "@id": "schema:author",
      "@type": "@id"
    }
  },
  "@id": "http://www.w3.org/TR/json-ld/",
  "@type": "bibo:Document",
  "author": {
    "@context": {
      "@vocab": "foaf",
      "homepage": {"@id": "foaf:homepage", "@type": "@id"}
    },
    "@type": ["foaf:Person", "schema:Person"]
    "name": "Manu Sporny",
    "homepage": "http://manu.sporny.org/"
  }
}

When compacting, term selection would consider node definitions having the (non-@context) content from @content as part of the matching criteria. (Note, this would need to place some limitations on nested node definitions).

This mechanism solves all of the above scenarios, and I believe is tractable without excessive complication in either compaction or expansion. It also addresses the framing issues where having different contexts be in effect for different parts of the frame.

If generally acceptable, this could be added to a forthcoming CG release of the specifications.

@gkellogg
Copy link
Member Author

gkellogg commented Sep 27, 2016

Some proposed text and examples for context processing and expansion:

JSON-LD Advanced Concepts adds a section on term content:

An expanded term definition in a JSON-LD context may have a @content property who's value must be a node object. This content is added to values of that term during expansion. A @context contained within that node object suplements any local context that may be present on that value, allowing the active term definitions, vocabulary, base, and language defaults to be applied as if they were defined directly within that node definition.

Example Adding a @content to a term definition to supplement a local context.

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {
        "@context": {
          "bar": "http://example.org/bar"
        }
      }
    }
  },
  "foo": {
    "bar": "baz"
  }
}

In this case, bar expands to http://example.org/bar instead of http://example/bar, because the context from @content is in scope within foo.

Example Adding a @content to a term definition to add members to a node object

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {"@content": {"@type": "Bar"}}
  },
  "foo": {"@type": "Foo"}
}

After expansion, Bar is added as a type for the value

[
  {
    "http://example/foo": [{
      "@type": ["http://example/Foo","http://example/Bar"]
    }]
  }
]

Changes to Context Processing

In the Create Term Definition algorithm add the following step before step 18

If value contains the key @content:

  1. If the value of @content is not a node object, or it contains the key @id, an invalid term content error has been detected and processing is aborted.
  2. If the term definition has @type, it must be @id or the term definition must contain @reverse, otherwise an invalid term content error has been detected and processing is aborted.
  3. Set content of definition to that value.

Changes to Expansion

In the Expansion Algorithm add the following before 7.1.

  1. If active property has an @content definition, which has an @context, merge it after any @context in element, by appending it to an array containing the value of @context from element, if necessary.

After step 7:

  1. Repeat step 7 using the value of @content from active property in place of element.
    1. Processors must ensure that content inclusion is not self-referential leading to an unending recursive expansion by noting a cyclic term content error and aborting processing.

Change 7.4.2 to allow for multiple properties to expand to @type.

Note that any @content contained within the content of a term definition will not affect processing for members of element, as processing is performed in a separate pass.

An alternative implementation would be to do a deep merge of element and value, which would allow certain other use cases and the expense of increased complexity.

Compaction

This is certainly more complicated, and requires deep matching of values based on finding all of the content from the expanded @content in the value being inspected for Term Selection, and extracting those values on compaction. More on this later.

Tests

Inherited term

{
  "@context": {
    "foo": {
      "@id": "http://example/foo",
      "@content": {
        "http://example.com/bar": "baz"
      }
    }
  },
  "foo": {}
}

expands to

[
  {
    "http://example/foo": [{"http://example.com/bar": [{"@value": "baz"}]}]
  }
]

Use of @type

{
  "@context": {
    "foo": {
      "@id": "http://example/foo",
      "@content": {
        "@type": "http://example.com/Bar"
      }
    }
  },
  "foo": {}
}

expands to

[
  {
    "http://example/foo": [{"@type": ["http://example.com/Bar"]}]
  }
]

@type and value with @type

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {"@content": {"@type": "Bar"}}
  },
  "foo": {"@type": "Foo"}
}

expands to

[
  {
    "http://example/foo": [{
      "@type": ["http://example/Foo","http://example/Bar"]
    }]
  }
]

@type and value with same @type

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {"@content": {"@type": "Foo"}}
  },
  "foo": {"@type": "Foo"}
}

expands to

[
  {
    "http://example/foo": [
      {"@type": ["http://example/Foo", "http://example/Foo"]}
    ]
  }
]

@context adding new term

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {
        "@context": {
          "bar": "http://example.org/bar"
        }
      }
    }
  },
  "foo": {
    "bar": "baz"
  }
}

expands to

[
  {
    "http://example/foo": [{"http://example.org/bar": [{"@value": "baz"}]}]
  }
]

@context overriding a term

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {
        "@context": {
          "bar": {"@type": "@id"}
        }
      }
    },
    "bar": {"@type": "http://www.w3.org/2001/XMLSchema#string"}
  },
  "foo": {
    "bar": "http://example/baz"
  }
}

expands to

[
  {
    "http://example/foo": [{"http://example/bar": [{"@id": "http://example/baz"}]}]
  }
]

property and value with different terms mapping to the same expanded property

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {
        "@context": {
          "Bar": {"@id": "bar"}
        },
        "Bar": "Baz"
      }
    }
  },
  "foo": {
    "bar": "baz"
  }
}

expands to

[
  {
    "http://example/foo": [{
      "http://example/bar": [
        {"@value": "baz"},
        {"@value": "Baz"}
      ]}
    ]
  }
]

property and value with same term

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {"bar": "Baz"}
    }
  },
  "foo": {
    "bar": "baz"
  }
}

expands to

[
  {
    "http://example/foo": [{
      "http://example/bar": [
        {"@value": "baz"},
        {"@value": "Baz"}
      ]}
    ]
  }
]

property with deep values

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {
        "bar": {
          "baz": "buzz"
        }
      }
    }
  },
  "foo": {
    "bar": "baz"
  }
}

expands to

[
  {
    "http://example/foo": [
      {
        "http://example/bar": [
          {"@value": "baz"},
          {"http://example/baz": [{"@value": "buzz"}]}
        ]
      }
    ]
  }
]

property with deep values and value with similar values

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {
        "bar": {
          "baz": "buzz"
        }
      }
    }
  },
  "foo": {
    "bar": {
      "baz": "Buzz"
    }
  }
}

expands to

[
  {
    "http://example/foo": [
      {
        "http://example/bar": [
          {"http://example/baz": [{"@value": "Buzz"}]},
          {"http://example/baz": [{"@value": "buzz"}]}
        ]
      }
    ]
  }
]

deep @context not affecting nested nodes (ed: this might not match expectations)

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {
        "@context": {
          "@context": {
            "baz": {"@type": "@id"}
          }
        }
      }
    }
  },
  "foo": {
    "bar": {
      "baz": "buzz"
    }
  }
}

expands to

[
  {
    "http://example/foo": [{
      "http://example/bar": [{
        "http://example/baz": [{"@value": "buzz"}]
      }]
    }]
  }
]

illegal content on a value

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {
        "bar": "baz"
      }
    }
  },
  "foo": {"@value": "Bar"}
}

Produces an invalid value object exception.

illegal content on a list

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {
        "bar": "baz"
      }
    }
  },
  "foo": {"@list": ["Bar"]}
}

Produces an invalid set or list object exception.

illegal content on a set

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {
        "bar": "baz"
      }
    }
  },
  "foo": {"@set": ["Bar"]}
}

Produces an invalid value object exception.

illegal @content as string

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": "true"
    }
  },
  "foo": {"@set": ["Bar"]}
}

Produces an invalid term content exception.

illegal @content as array

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": []
    }
  },
  "foo": {"@set": ["Bar"]}
}

Produces an invalid term content exception.

illegal term definition with @content and bad @type

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@type": "http://www.w3.org/2001/XMLSchema#string",
      "@content": {}
    }
  },
  "foo": {"@set": ["Bar"]}
}

Produces an invalid term content exception.

illegal recursive use of term with content

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {
        "@value": "buzz"
      }
    }
  },
  "foo": {"bar": "baz"}
}

Produces an invalid term content exception.

illegal content on a list

{
  "@context": {
    "@vocab": "http://example/",
    "foo": {
      "@content": {
        "foo": {"bar": "buzz"}
      }
    }
  },
  "foo": {"bar": "baz"}
}

Produces an cyclic term content exception.

gkellogg added a commit to ruby-rdf/json-ld that referenced this issue Sep 27, 2016
@gkellogg
Copy link
Member Author

I made a minor edit to allow multiple properties to expand to @type, which also allows @type to b specified both in element and content.

@niklasl
Copy link
Member

niklasl commented Oct 5, 2016

It is great to see you picking this up again, Gregg! I fear I have few spare cycles, but I'd like to contribute where I can. I imagine two alternative paths forward for this specific issue:

Just Nested Context

It seems @content is really only needed for one of the requested cases (for applying a default type)? Isn't the simpler solution then to just support @context directly for terms, and not allow generated content in general?

And (as an independent change) to allow @type "coercion" to be generalized (i.e. adding the @type value even in non-literal nodes)?

Content Templates

If the latter (generated content) is to be pursued, I really think being able to capture a simple property value and then expand it in the desired slot would be useful, making the @content data into a simple template/macro mechanism. So that, given this context:

{
  "@context": {
    "title": {
      "@id": "hasTitle",
      "@template": {
        "@type": "InstanceTitle",
        "@slot": "mainTitle"
      }
    }
  }
}

data like:

{
  "title": "An Example"
}

turns into:

{
  "hasTitle": {"@type": "InstanceTitle", "mainTitle": "An Example"}
}

(You can imagine similar cases of turning author strings into linked author items with a name property, or simple string values into structured values using @type and rdf:value along with some unit designation.)

(If that was possible, I could pursue the generating of JSON-LD contexts from OWL ontologies even further (dealing with e.g. owl:propertyChainAxiom and certain kinds of owl:hasValue restrictions), which I see as an emerging need...)

That said, this might just be too complex. It's just that stopping "half way" with @content expansion as proposed might add lots of complexity for one perceived need, without solving closely related challenges. (And if we'd explore a content generation mechanism, we're heading into possibly related use cases like #246, #269 and #271.)

I'd say going down this path also calls for a one-way application of certain contexts, meaning that they might not be cheap to apply for automatic compaction. That remains to be seen though. (If this where to be the result, it might open up for old cases like expanding one term into multiple properties, or having multiple aliases for the same property.)

(This might also be valuable for applying JSON-LD contexts directly on e.g. CSV automatically turned into JSON (using e.g. the csv.DictReader in Python).)

@gkellogg
Copy link
Member Author

gkellogg commented Oct 5, 2016

Hi @niklasl, I welcome your insights and any amount of time you have to contribute!

It seems @content is really only needed for one of the requested cases (for applying a default type)? Isn't the simpler solution then to just support @context directly for terms, and not allow generated content in general?

There are more use cases that came up in comments on the mailing list, I just highlighted one to provide a concrete example. I believe that both adding content to nodes and a nested @context are important use cases to contribute are important, but they could be two different mechanisms. People seem to broadly think that information can be provided in a term definition that will expand for values of that term.

Looking at the expansion algorithm in particular, @context and other content are handled differently, however there is a question of what context is in scope for handling other content, and how would @context included with that content be treated. But, it seems like a common "mistake" that people might make, so having some rules for how @context as a top-level property within a term definition should be handled probably makes sense.

And (as an independent change) to allow @type "coercion" to be generalized (i.e. adding the @type value even in non-literal nodes)?

That's certainly something to consider, probably also using something like @content, but applied to value objects.

Content Templates

The semantics of this might be a little more specialized, and are probably a different feature than @content. This might be quite a bit to bite off for a 1.1 spec, if it is done in addition to @content. But, creating a comprehensive proposal would be useful, if you're so inclined. I agree that compaction becomes even more challenging, and once templating is introduced, the kinds of templates applied might become more and more complex. Another alternative would be a separate spec for transforming JSON-LD, or JSON-LD Templating.

Might content template use cases be handled by framing? However, this would probably require the introduction of variables in the Frame language, making it that much closer to SPARQL.

(This might also be valuable for applying JSON-LD contexts directly on e.g. CSV automatically turned into JSON (using e.g. the csv.DictReader in Python).)

I presume you've followed the CSV on the Web work, which essentially uses JSON-LD metadata to turn CSV into JSON or RDF.

@gkellogg
Copy link
Member Author

gkellogg commented Apr 8, 2017

Doesn't seem there's much interest in doing this; I propose closing this issue; please 👍 or 👎 to close.

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

No branches or pull requests

2 participants