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

Should @id of parent objects be used as a base IRI for relative IRI references? #447

Open
trwnh opened this issue Nov 18, 2024 · 4 comments

Comments

@trwnh
Copy link

trwnh commented Nov 18, 2024

By my understanding, the way to absolutize a relative IRI reference in JSON-LD is currently something like this:

  • if the JsonLdProcessor has JsonLdOptions.base set, then use that.
  • otherwise, if the active context includes a @base declaration, then use that.
  • otherwise, if the document was fetched from a remote location, then the base IRI can be inferred by the IRI of the request.
    • if the response was HTML, then maybe <base> can be used?
  • otherwise, emit a warning?

However, there is a possibility for objects to be nested or embedded in other objects. Especially in non-flattened form.

I would interpret a document like so:

{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/multikey/v1",
    "https://w3id.org/security/suites/secp256k1-2019/v1"
  ],
  "alsoKnownAs": [
    "at://atproto.com"
  ],
  "id": "did:plc:ewvi7nxzyoun6zhxrhs64oiz",
  "service": [
    {
      "id": "#atproto_pds",  // this is a relative reference, right?
      "serviceEndpoint": "https://enoki.us-east.host.bsky.network",
      "type": "AtprotoPersonalDataServer"
    }
  ],
  "verificationMethod": [
    {
      "controller": "did:plc:ewvi7nxzyoun6zhxrhs64oiz",
      "id": "did:plc:ewvi7nxzyoun6zhxrhs64oiz#atproto",
      "publicKeyMultibase": "zQ3shunBKsXixLxKtC5qeSG9E4J5RkGN57im31pcTzbNQnm5w",
      "type": "Multikey"
    }
  ]
}

where it seems like the node with "id": "#atproto_pds" is part of its parent context, the node with "id": "did:plc:ewvi7nxzyoun6zhxrhs64oiz"... which I think represents the document?

So the question is: Should the @id of parent objects be considered in the expansion of relative IRI references? If not, why not? If yes, should we consider only the top-level parent or should we instead consider the most recent absolutized parent?

@gkellogg
Copy link
Member

Actually, I think that the in-scope @base has highest priority, followed by a base specified via API option, and then the document location. This is pretty consistent with RFC3986.

Regarding cascading objects and relative IRIs, IMO, this would be too sophisticated for the JSON-LD processing model, and has no basis in RFC3986 URI resolution.

@gkellogg gkellogg moved this to Discuss-Call in JSON-LD Management Mar 10, 2025
@anatoly-scherbakov
Copy link

The same JSON-LD document can be represented in different forms, meaning we could reverse the roles of the parent object and the nested object and keep the document's meaning (i.e., its graph form) the same.

Introducing such a convention would make such manipulations more difficult. Explicit is better than implicit ⇒ Perhaps this rule would be too implicit IMHO.

@trwnh
Copy link
Author

trwnh commented Mar 13, 2025

I think that makes sense:

  1. @base
  2. JsonLdOptions.base
  3. current network location (implied to be generally the same as the top-level id)

The part that I am still confused about is with embedding, this implies that you should inject a @base or else relative references within a subnode will be relative to the incorrect base?

Meaning that if you dereference https://thing1.example and get:

{
  "@id": "https://thing1.example",
  "https://relation1.example": {
    "@id": "https://thing2.example",
    "https://relation2.example": {
      "@id": "#relative"  // thing1.example/#relative
    }
  }
}

then #relative expands to https://thing1.example/#relative? But if you dereference https://thing2.example and get:

{
  "@id": "https://thing2.example",
  "https://relation2.example": {
    "@id": "#relative"  // thing2.example/#relative
  }
}

then #relative expands to https://thing2.example/#relative? So in order to preserve the correct expanded id, if you dereference https://thing1.example and get this:

{
  "@id": "https://thing1.example",
  "https://relation1.example": {
    "@id": "https://thing2.example"  // this will be fetched and embedded later
  }
}

then it is not enough to simply replace id-references with the document as-is. You would need to formulate a document like so:

{
  "@id": "https://thing1.example",
  "https://relation1.example": {
    "@context": {  // we inject this to fix the relative reference
      "@base": "https://thing2.example/"  // note the trailing slash
    },
    "@id": "https://thing2.example",
    "https://relation2.example": {
      "@id": "#relative"  // fixed, it is now thing2.example/#relative
    }
  }
}

This is a pitfall when it comes to relative references and hydration / embedding. I guess it applies to JSON-LD Best Practices? "To preserve semantics, when fetching a document over the network that contains relative references, you should inject a base IRI declaration before embedding that representation?"

@gkellogg
Copy link
Member

You could achieve the nested-base using property- or type-scoped contexts, but that would be obscure and error prone.

The fact is, that RFC3986 is pretty clear on how to expand relative URIs by finding an appropriate base.

The base URI can be default, the document location, the Base URI of an encapsulating entity, of a Base URI embedded in the content (i.e., @base in a context on an ancestor object).

There is no precedent for having the URI of an entity becoming the base for nested relative URIs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Discuss-Call
Development

No branches or pull requests

3 participants