Repository navigation
Skip the encoder for null optional elements that write nothing - #83
Merged
veewee merged 8 commits intoOct 16, 2026
Merged
Conversation
A null value on an optional element that is not nillable goes through ErrorHandlingEncoder and OptionalElementEncoder only to come back as an empty string. ObjectAccess now records per property whether that is the case, and ObjectEncoder skips the iso for those nulls. The empty write still closes the start tag of the parent, so the first skipped element of an object keeps it to write <a></a> as before. Custom encoders that handle null themselves are not wrapped in the optional element encoder and keep their output.
Every expectation also holds on the base branch, so the skip leaves the output of these cases as it was.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ObjectAccess no longer depends on ErrorHandlingEncoder wrapping OptionalElementEncoder directly. An encoder that implements WritesNothingForNull answers for itself, ErrorHandlingEncoder forwards the question, and any other encoder keeps its own null handling.
An object whose elements are all null now encodes as <a/> instead of <a></a>. Both are the same empty element; the short form is what an object without children already gets elsewhere.
Two booleans per property, one for absent nillable elements and one for elements that write nothing, described a single decision from two sides. The encoder now answers it once through NullValueAware with a NullValueHandling case, and ObjectAccess keeps one map of those answers. A custom OptionalAware encoder that is not wrapped in OptionalElementEncoder no longer has its absent optional nillable elements left out, unless it implements NullValueAware itself, because only the encoder can tell what it writes for null.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #79, so it targets that branch until it is merged.
Null optional elements on encode
A null value on an optional element that is not nillable goes through
ErrorHandlingEncoderandOptionalElementEncoder, only to come back as an empty string that writes nothing. On sparse payloads that is most of the elements, and it is the largest cost left in sparse encoding.ObjectAccess::buildnow asks the encoder of every child element once per type what a null value does, and keeps the answer in oneonNullmap. The answer is aNullValueHandlingcase:Writelets the encoder write it,Skipmeans the encoder writes nothing for null, andSkipWhenAbsentleaves the element out when the property is absent and writes xsi:nil when it is set to null. Encoders answer through a newFeature\NullValueAwareinterface.OptionalElementEncoderanswersSkipfor a nullable element that is not nillable, the same rule its iso uses,SkipWhenAbsentfor an optional nillable element, andWriteotherwise.ErrorHandlingEncoderforwards the question. Attributes and the_text value always getWrite, and a custom encoder lens never counts as absent, as in #79.ObjectEncoder::tohas one null check that reads this map, and replaces the absent check from #79 as well.Same output
This does not depend on how the encoders are nested. Any encoder that does not implement the interface keeps its own null handling, so a custom encoder that writes something for null is never skipped, and a custom encoder can opt in. Custom encoder lenses still run before the check. One thing does change compared to #79: a custom
OptionalAwareencoder that is not wrapped inOptionalElementEncoderno longer has its absent optional nillable elements left out unless it implementsNullValueAware, because only the encoder knows what it writes for null.One output detail changes: an object whose elements are all null encoded to
<a></a>and now encodes to<a/>. Both are the same empty element, and an object whose nillable optional elements are all absent already gets<a/>since #79. The tests pin the output byte for byte for all null, all absent, leading and trailing nulls, a nested empty object and a custom encoder. The encoded requests and decoded results of the 3MB workloads have the same sha1 before and after, because none of their objects is empty.Speed
Base is
absent-optional-elements(#79). Medians over three interleaved rounds on the same machine, with ext-soap as the noise reference.The first two rows are the target and drop by about a third. EncodeSparseBench uses nillable elements, which this change does not skip, so those rows and the others stay within the run to run spread.