Skip to content

Build xPression XML

Serializes an XDesignXElement/XDesignXDocument tree into the xPression-dialect XML string consumed by the print/preview/distribute gateway, propagating primary and foreign keys down through nested repeating sections before rendering.

Field Value
Operation ID OP-11
Trigger ToXpression() invoked (directly on an XDesignXDocument/XDesignXElement, or indirectly via ReportBase.Render)
Inputs An XDesignXElement/XDesignXDocument tree with keys/foreign keys set via AddKey
Outputs xPression-dialect XML string
Primary components XDesignXDocument.ToXpression, XDesignXElement.ToXpression, PropagateKeys, RenderElement/RenderStartElement/RenderEndElement
Entry point Public Function ToXpression() As String (XDesignXDocument.vb); Public Function ToXpression(Optional tabIndex As Integer = 0) As String (XDesignXElement.vb)

Steps

  1. XDesignXDocument.ToXpression() checks that Me.Root is not Nothing and that it is a XDesignXElement (BR-PRT-019).
  2. If either condition fails: returns an empty string immediately, without attempting any serialization.
  3. Otherwise delegates to CType(Me.Root, XDesignXElement).ToXpression(), the element-level recursive serializer, starting at tabIndex=0.
  4. XDesignXElement.ToXpression(tabIndex), on the first (top-level) call only (tabIndex=0), invokes PropagateKeys() once for the whole tree before rendering begins (BR-PRT-016).
  5. PropagateKeys walks all descendant XDesignXElements recursively: for each descendant element that itself has children, it clears that element's existing foreign keys, then adds every one of the current element's own primary keys plus every one of the current element's already-accumulated foreign keys as new foreign keys on the descendant, then recurses into the descendant.*
  6. If the current element has no children (Elements.Count = 0): renders as a single leaf via RenderElement(tabIndex, Name.LocalName, Value).
  7. If Value is an empty string: emits a self-closing tag (<Name/>) instead of an empty-value pair of tags (BR-PRT-017).
  8. If Value is non-empty: escapes it via System.Security.SecurityElement.Escape before emitting <Name>escapedValue</Name> (BR-PRT-018).
  9. If the current element has children: renders a start tag via RenderStartElement, then for each accumulated foreign key emits it as a child leaf element via RenderElement (itself subject to BR-PRT-017 / BR-PRT-018), then recurses into each real child element (ToXpression(tabIndex+1)).
  10. Non-XDesignXElement children are first converted via the ToSAIFXElement extension method before recursing.
  11. After all children are rendered, emits the end tag via RenderEndElement.
  12. Returns the fully concatenated string up the recursion chain to the original XDesignXDocument.ToXpression() caller.

Process flow

The following diagram visualizes this operation's control flow, from the document-level malformed-input guard through the element-level recursive serialization.

flowchart TD
    Start(["ToXpression called<br>on Document"]) --> Guard{"Root exists AND<br>is XDesignXElement?"}
    Guard -->|No| EndEmpty(["Return empty string"])
    Guard -->|Yes| Element["Element.ToXpression(0)"]
    Element --> TopCheck{"tabIndex = 0?"}
    TopCheck -->|Yes| Propagate["PropagateKeys() once"]
    TopCheck -->|No| Children
    Propagate --> Children{"Has children?"}
    Children -->|No| Leaf["RenderElement leaf"]
    Leaf --> ValueCheck{"Value empty?"}
    ValueCheck -->|Yes| SelfClose["Self-closing tag"]
    ValueCheck -->|No| Escape["Escape and emit<br>text node"]
    SelfClose --> EndLeaf(["Return leaf string"])
    Escape --> EndLeaf
    Children -->|Yes| StartTag["RenderStartElement"]
    StartTag --> Keys["Emit foreign keys<br>as leaf elements"]
    Keys --> Recurse["Recurse into each<br>child element"]
    Recurse --> EndTag["RenderEndElement"]
    EndTag --> EndBranch(["Return concatenated string"])

Failure and edge-case behavior

  • A document with no root, or whose root is not an XDesignXElement, serializes silently to an empty string — a caller cannot distinguish "empty document" from "malformed document" from the return value alone (BR-PRT-019).
  • PropagateKeys runs only once per top-level ToXpression() call, guarded by the tabIndex=0 check. Calling ToXpression(tabIndex:=N) directly on a sub-element with N > 0 skips key propagation entirely, which would leave that sub-tree's foreign keys stale or unpropagated if invoked out of the normal top-down sequence — an easy-to-misuse API surface.*
  • RenderElement's empty-value comparison performs no null-check on Me.Value before comparing it to ""; this relies on XElement.Value never returning Nothing, a .NET framework guarantee rather than a printing-library rule.

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with ** could not be confirmed; treat as unknown until verified.

Sources

  • SAIF.Services.Printing/Reporting/XDesignXElement.vb
  • SAIF.Services.Printing/Reporting/XDesignXDocument.vb