Skip to content

Fix: xs:element default value ignored for simpleContent extension types - #285

Draft
Bergmann89 with Copilot wants to merge 3 commits into
masterfrom
copilot/fix-xs-boolean-default-value
Draft

Fix: xs:element default value ignored for simpleContent extension types#285
Bergmann89 with Copilot wants to merge 3 commits into
masterfrom
copilot/fix-xs-boolean-default-value

Conversation

Copilot AI commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

When an xs:element carries default="false" and wraps an inline complex type with xs:simpleContent (e.g. extending xs:boolean), a self-closing element like <Bar Baz="xxxx"/> fails deserialization with UnknownOrInvalidValue("") — the default is never applied.

Root cause: Event::Empty (self-closing XML element) was passed directly to ContentDeserializer::init, which tried to parse an empty byte slice as a boolean. The default attribute from xs:element was never threaded through the pipeline to the generated deserializer.

Changes

  • ComplexMeta (models/meta/complex.rs): Add default: Option<String> field; include in TypeEq so types with different defaults remain distinct.
  • Interpreter (pipeline/interpreter/state/generate_types/variant_processor.rs): Propagate xs:element.defaultComplexMeta.default when building inline complex types.
  • Generator (pipeline/generator/data/complex.rs): Convert the default string to a ValueRendererBox; store it in ComplexDataContent.default_value. Thread a default: Option<&str> parameter through new(), new_simple(), and all call sites.
  • ComplexDataContent (models/data/complex.rs): Add default_value: Option<ValueRendererBox> field.
  • Defaults renderer (pipeline/renderer/steps/defaults.rs): Emit a pub fn default_content() -> T { … } associated function when default_value is set.
  • Deserializer renderer (pipeline/renderer/steps/quick_xml/deserialize.rs): In render_deserializer_fn_next_content_simple, intercept Event::Empty before calling ContentDeserializer — if a default exists, call default_content() and return immediately.

Generated output example

impl BarElementType {
    #[must_use]
    pub fn default_content() -> bool {
        false
    }
}

// In the generated deserializer:
S::Init__ => {
    if matches!(&event, Event::Empty(_)) {
        self.store_content(super::BarElementType::default_content())?;
        let data = self.finish(helper)?;
        return Ok(DeserializerOutput { artifact: DeserializerArtifact::Data(data), .. });
    }
    let output = ContentDeserializer::init(helper, event)?;
    self.handle_content(helper, output)
}

Thread `default` attribute from xs:element through meta-model, generator,
and renderer to produce `default_content()` and use it in the generated
deserializer for `Event::Empty` (self-closing element with no text content).

Changes:
- ComplexMeta: add `default: Option<String>` field
- Interpreter: propagate xs:element `default` to inline ComplexMeta
- Generator: pass default through to ComplexDataContent as ValueRendererBox
- Renderer (defaults.rs): render `default_content()` method
- Renderer (deserialize.rs): intercept Event::Empty and use default_content()
- New feature test for xs:boolean + simpleContent default
- Update ONIX expected output (also has an element with default)
Copilot AI changed the title [WIP] Fix issue with xs:boolean element ignoring default value Fix: xs:element default value ignored for simpleContent extension types Jul 24, 2026
Copilot AI requested a review from Bergmann89 July 24, 2026 11:48
@Bergmann89

Copy link
Copy Markdown
Owner

@copilot Check the latest comment in the issue. There is still an edge case, that does not work correctly.

Copilot AI commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Check the latest comment in the issue. There is still an edge case, that does not work correctly.

Fixed in commit 3e8d9ae. The edge case was <Bar Baz="xxxx"></Bar> (explicit open+close tags with no content) — Event::Start was forwarded to ContentDeserializer::init which left an empty accumulator, and the following Event::End then tried to parse "" as bool, failing.

The fix introduces a ContentInit__ state (generated only when simple content has a default value). After receiving Event::Start the deserializer transitions to ContentInit__ instead of Content__. If the very next event is Event::End — meaning no text was written — default_content() is used instead of attempting to parse an empty string.

Both forms now work:

  • <Bar Baz="x"/>Event::Empty → uses default (existing handler)
  • <Bar Baz="x"></Bar>Event::Start + Event::End → uses default (new ContentInit__ arm)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Element with extension base type "xs:boolean" ignores default value

2 participants