warriordog / ActivityPubSharp

Modular implementation of ActivityPub in C#
https://warriordog.github.io/ActivityPubSharp/
Mozilla Public License 2.0
45 stars 9 forks source link

JSON-LD context is not passed to nested objects #152

Closed warriordog closed 7 months ago

warriordog commented 9 months ago

The JSON-LD context is lost when converting a complex object with custom contexts, extensions, and nested types. We need to preserve this object and pass it down into the nested objects. This will be extremely non-trivial.

Proposal: use bad idea 4

warriordog commented 8 months ago

@jenniferplusplus do you have any ideas for this? It has potential to break on real-world messages like a collection of actors. I can think of at least one hacky solution using a thread-local Stack, but I'm hoping to find something cleaner.

jenniferplusplus commented 8 months ago

@warriordog I have given this a little thought from time to time, and I don't have any good ideas, only bad ones.

bad idea 1

Introduce the concept of a json-ld document, to which objects are added. The add methods for the document extract the contexts from the added objects so the document can track them easily. Those objects would then presumably also need to know which document they're attached to, to permit the same behavior on nested objects. It might make sense for the document to be a kind of factory, to help with creating those objects with the correct document association.

bad idea 2

It's actually intended behavior that JSON-LD objects can include local @context that would only apply locally and could override an ancestor @context. Which is, uh, a choice. We could just do that. Let nested objects serialize with their own @context (possibly minus the default AS context) and just accept that this is probably not properly compacted.

(re-reading the original issue, and I'm not sure option 2 actually addresses the problem, if this is about deserializing)

jenniferplusplus commented 8 months ago

FWIW, I doubt that Letterbook would ever care about a collection of actors (or anything, really), beyond their IDs. We would almost certainly just extract the IDs and then go fetch the objects individually from their canonical sources.

JSON-LD is an absolutely bonkers standard to have to implement for received input from arbitrary sources. Processing it correctly is more like compiling than parsing, and I'm entirely unwilling to do that, for lots of reasons. I'm willing to work around most interop challenges that might create, particularly since it's not even possible to do valid LD processing on real world documents anyway, since a great many of their referenced contexts don't resolve to anything.

warriordog commented 8 months ago

@jenniferplusplus thanks for the thoughts. I'm going to keep working on this, but it's good to know that it wont be a blocker.

warriordog commented 8 months ago

Bad idea 3:

Summary:

Refactor JsonLDContext to reference an optional "parent" context. Modify all properties that accept an object so that they bind the parent context when the object is attached. Wrap this up into a tidy little NestedObject<TModel> type for ease of implementation. When serializing, skip the parent context. Additionally, skip any entries that exist in the parent. This will result in a JSON-LD object that has nested contexts only where they're needed. Deserialization does not require any changes.

Pros:

Cons:

warriordog commented 8 months ago

Bad idea 4

Summary

This is the idea mentioned in the first comment above. First, refactor JsonLDContext as described in idea 3. Keep the same serialization changes. Additionally, modify TypeMapConverter to add a thread-local Stack<IJsonLDContext> ParentContext property. Whenever a JsonLDContext is parsed from a message, peek at the stack and set the instance (if present) as the parent context. Then, push the current context to the stack. When returning from the converter, pop the stack. This should be safe since System.Text.Json works in-order across the JSON message.

Pros

Cons