w3c / json-ld-syntax

JSON-LD 1.1 Specification
https://w3c.github.io/json-ld-syntax/
Other
112 stars 22 forks source link

Additional restriction to `@sealed` term clearing #136

Closed dlongley closed 5 years ago

dlongley commented 5 years ago

We've completed a preliminary implementation of @sealed terms here:

https://github.com/digitalbazaar/jsonld.js/pull/289

Our implementation:

Note that the last item is a slight tweak (additional restriction) on what is in the spec. We believe this approach better matches the use cases and expectations of the target audience for JSON-only application developers. The high-level idea is that a @sealed term is assured to maintain its meaning unless cleared by a new term (with a scoped context) that creates a new branch in the JSON tree.

This approach:

We'd like to see the spec updated to restrict clearing @sealed terms in the way articulated here and implemented in the above link. This approach seems to fully address the original need for the feature and has a simple implementation.

gkellogg commented 5 years ago

This looks like a middle ground between keeping track of sealed terms from different contexts and providing some impediment to simply wiping out sealed terms, which only takes effect when you've stepped into a different part of the JSON tree.

iherman commented 5 years ago

@dlongley, thanks. Just for my understanding, can you propose alternate versions of the examples 42-44? I am. e.g., not sure what you mean by "clearing".

Looking, e.g., in Example 42: does it mean that the second attempt to overwrite a term (namely as part of "knows") is acceptable in contrast to what the example say now? Or is this action permitted only for a term different from "knows", i.e., a "new" term?

My feeling is that you mean the second option. But I am not sure…

dlongley commented 5 years ago

@iherman,

I am. e.g., not sure what you mean by "clearing".

I mean that the active context is reset to the initial active context (a "clean slate" having no term definitions). If there are sealed terms in the active context when an attempt is made to reset it, this will fail unless the attempt that is made is via a term definition with a scoped context and that term is used as a property (as opposed to as a value for @type). More explanation to follow...

does it mean that the second attempt to overwrite a term (namely as part of "knows") is acceptable in contrast to what the example say now? Or is this action permitted only for a term different from "knows", i.e., a "new" term?

Actually, the current examples in the spec don't change at all; they are still correct with this new restriction. So, no, it is still not acceptable and, yes, I mean "the second option" as you suspected. You must create a new term just like Example 44 does. However, this makes it more clear that the only acceptable way is the way Example 44 does it.

For instance, an example of something that would not be acceptable is this:

{
  "@context": [
    {
      "@version": 1.1,
      "@sealed": true,
      "Organization": "http://schema.org/Organization",
      "name": "http://schema.org/name",
      "employee": "http://schema.org/employee"
    }
  ],
  "@type": "Organization",
  "name": "Digital Bazaar",
  "employee" : {
    "@context": [
       // because of "@context": null in an embedded context 
       // and not a scoped context, this `null` will fail and so
       // would the override attempt of name
      null,
      {
        "name": "this_attempt_to_override_name_will_fail"
      }
    ],
    "name": "Manu Sporny"
  }
}

This above example would continue to fail even if employee were not sealed or if a new term were introduced that did not use a scoped context:

{
  "@context": [
    {
      "@version": 1.1,
      "@sealed": true,
      "Organization": "http://schema.org/Organization",
      "name": "http://schema.org/name",
      "employee": "http://schema.org/employee"
    },
    {
      "newButNoScopedContext": "http://example.com/newButNoScopedContext"
    }
  ],
  "@type": "Organization",
  "name": "Digital Bazaar",
  "newButNoScopedContext" : {
    "@context": [
       // because of "@context": null in an embedded context 
       // and not a scoped context, this `null` will fail and so
       // would the override attempt of name
      null,
      {
        "name": "this_attempt_to_override_name_will_fail"
      }
    ],
    "name": "Manu Sporny"
  }
}

Similarly, this would fail:

{
  "@context": [
    {
      "@version": 1.1,
      "@sealed": true,
      "Organization": "http://schema.org/Organization",
      "name": "http://schema.org/name",
      "employee": "http://schema.org/employee",
      "SpecialOrganization": {
        "@id": "http://example.com/SpecialOrganization",
        // if the term for this definition is used as an "@type",
        // then this attempt to clear "@sealed" terms will fail
        "@context": [
          null,
          {
            "name": "this_attempt_to_override_name_will_fail",
            "employee": "this_attempt_to_override_name_will_fail"
          }
        ]
      }
    }
  ],
  // because of "@context": null in a scoped context for
  // a term used as an "@type" rather than as a property, its
  // term definition will fail
  "@type": ["Organization", "SpecialOrganization"],
  "name": "Digital Bazaar",
  "employee" : {
    "name": "Manu Sporny"
  }
}
iherman commented 5 years ago

@dlongley still trying to understand... If I did the following (sorry for the ugly code, but it is legal...)

{
  "@context": [
    {
      "@version": 1.1,
      "@sealed": true,
      "Organization": "http://schema.org/Organization",
      "name": "http://schema.org/name",
      "employee": "http://schema.org/employee",
      "SpecialOrganization": {
        "@id": "http://example.com/SpecialOrganization",
        // if the term for this definition is used as an "@type",
        // then this attempt to clear "@sealed" terms will fail
        "@context": [
          null,
          {
            "name": "this_attempt_to_override_name_will_fail",
            "employee": "this_attempt_to_override_name_will_fail"
          }
        ]
      }
    }
  ],
  // because of "@context": null in a scoped context for
  // a term used as an "@type" rather than as a property, its
  // term definition will fail
  "@type": ["Organization", "SpecialOrganization"],
  "name": "Digital Bazaar",
  "employee" : {
    "name": "Manu Sporny"
  },
  "SpecialOrganization" : {
      "name" : "somebody"
  }
}

then is it so that the second occurence of "SpecialOrganization" will not fail because I used "SpecialOrganization" as a term this time?

pchampin commented 5 years ago

I like this new proposal.

If we are to accept it, I would update the spec text, because the paragraph before Example 44 would not be correct anymore. It would have to make it clear why Example 44 works, and in which cases "@context": null would fail...

dlongley commented 5 years ago

@iherman,

then is it so that the second occurence of "SpecialOrganization" will not fail because I used "SpecialOrganization" as a term this time?

Yes, it would be legal there. Of course, since SpecialOrganization still appears in @type in your example, a JSON-LD processor configured to throw errors would have already thrown an error for that occurrence.

iherman commented 5 years ago

Would that be an error, like stop processing, or a warning, like ignoring the changes?

Anyway, that is another matter, I think I understand what you are getting at. (Until I am lost again, that is:-) I agree with @pchampin that the text in the spec should be updated asap, to see if the full ~@seal~ @protected feature can be explained clearly...

dlongley commented 5 years ago

Would that be an error, like stop processing, or a warning, like ignoring the changes?

Anyway, that is another matter, I think I understand what you are getting at.

I agree that's another issue. I suspect we might want processors to support two modes: a default mode that raises an error (stops processing) and an optional mode to instead issue a warning that the attempted changes were invalid and thus ignored.

azaroth42 commented 5 years ago

I disagree that the definition provided matches the examples. You can have scoped contexts based on @type, so the SpecialOrganization example should NOT fail. Otherwise you're introducing even more special cases by allowing property scoped contexts to override @protected but type scoped contexts cannot.

This needs more work.

dlongley commented 5 years ago

You can have scoped contexts based on @type, so the SpecialOrganization example should NOT fail.

No, this is not permitted as it would break the protection rules.

Otherwise you're introducing even more special cases by allowing property scoped contexts to override @protected but type scoped contexts cannot.

I disagree. There are not several special cases. There is only one way to override @protected and it is through property term scoped contexts.

This is chosen because it is the only safe way to do it -- and it matches expectations and intuitions for what "protection" means. The reason @protection is added to a term is to enable users to safely consume JSON-LD as regular JSON (i.e. idiomatically and without having to use JSON-LD processing).

Since JSON developers rely on the structure of the JSON tree to understand meaning, it makes sense that adding and removing @protection depends on this. If @protection can be removed, it must only be done in a way that would not break this understanding. Therefore, protection can be safely removed by introducing a new section of the JSON tree via a scoped @context -- no other way. This approach mirrors how idiomatic JSON extensions are done: by extending the JSON tree.

azaroth42 commented 5 years ago

Since JSON developers rely on the structure of the JSON tree to understand meaning,

Not necessarily. A perfectly comprehensible JSON pattern would be to instantiate the JSON objects as objects, and then just understand the meaning based on the classes. This would imply that an @type based pattern is just as useful as a predicate based pattern.

The special cases are:

dlongley commented 5 years ago

I think an @type based pattern is fine, however, when there are conflicts with existing @protected terms, processing will fail. You cannot mix types with existing meaning, just like you'd have trouble doing multiple inheritance (or it is prohibited in many languages). So you can't erase existing @protected terms using @type scoped term definitions.

iherman commented 5 years ago

This issue was discussed in a meeting.