xAPI serialization¶
One file, XApiJsonWriter, which turns an XApiStatement into the JSON an LRS will accept. It is internal — nothing outside the package should need it.
Why this is hand-written¶
JsonUtility cannot express an xAPI statement. Three reasons, each fatal on its own:
It has no concept of omission. Unity serializes every field it can see. A nullable bool? is not serialized at all, and a plain bool is always serialized — so JsonUtility can produce "success": false for a statement whose author never mentioned success, which is a false claim about the learner, or drop the field entirely when it was deliberately set. xAPI needs "say nothing" and "say no" to be different, and this writer omits a null rather than emitting a default.
Some JSON keys are not valid C# identifiers. The language map key is en-US, with a hyphen. The field has to be called en_US in C#, and the writer renames it on the way out. JsonUtility has no hook for that.
Numbers must be culture-invariant. float.ToString() under a locale with a comma decimal separator produces 0,85, which is not a JSON number. Every number here goes through CultureInfo.InvariantCulture. This is not theoretical — the same bug existed on the SCORM read path and was found by a test running under a comma-separator culture.
Shape¶
A small JsonObjectBuilder collects "key":value pairs and joins them, with AddString, AddBool, AddNumber and AddObject for nesting. Each skips a blank key, and AddString escapes both key and value.
It is a writer, not a parser. Nothing in the package reads a statement back — statements go one way, and the only response that matters is the status code.