SCXML Data Models

SCXML Data Models

VSCXML supports multiple data model types for managing state machine data and expressions. Choose the appropriate data model based on your requirements.

Quick Comparison

Data Model Use Case Type Safety Performance Complexity
Simple Small state machines, no complex logic ❌ Runtime ⚡ Fast ✅ Low
native-java Type-safe Java, compile-time checks ✅ Compile-time ⚡⚡ Fastest ⚠️ Medium
native-js Native JavaScript for JS runtime ❌ Runtime ⚡⚡ Fastest ⚠️ Medium
native-go Type-safe Go, compile-time checks ✅ Compile-time ⚡⚡ Fastest ⚠️ Medium
ECMAScript Dynamic scripting, complex logic ❌ Runtime ⚠️ Moderate ⚠️ Medium
XPath XML data processing ❌ Runtime ⚠️ Moderate ❌ High
Null No data model needed N/A ⚡⚡ Fastest ✅ Lowest

Native Datamodel Naming Convention

Native datamodels are target-language-specific and follow the naming pattern: native-{language}

Datamodel Target Language Description
native-java Java Compile-time Java code generation with typed variables
native-js JavaScript Native JavaScript expressions for JS runtime
native-csharp C# Compile-time C# code generation with typed variables
native-c C Native C expressions for embedded C transpiler
native-python Python Compile-time Python code with type hints
native-go Go Compile-time Go code generation with typed variables
native-cpp C++ Native C++ expressions (future)

This naming convention ensures:

  1. Clear identification of target language
  2. Consistent naming across all platforms
  3. Automatic skipping of incompatible tests (e.g., native-java tests skip on JavaScript runtime)

Simple Data Model

Runtime-evaluated expressions with a simple evaluator.

When to Use

  • Small state machines with basic data
  • Simple arithmetic and string operations
  • Quick prototyping
  • No complex scripting needed

Example

xml
<scxml datamodel="simple" initial="start">
    <datamodel>
        <data id="count" expr="0"/>
        <data id="name" expr="Alice"/>
    </datamodel>

    <state id="start">
        <onentry>
            <assign location="count" expr="count + 1"/>
            <log label="Count" expr="count"/>
        </onentry>
        <transition cond="count == 5" target="done"/>
    </state>

    <final id="done"/>
</scxml>

Supported Operations

  • Arithmetic: +, -, *, /, %
  • Comparison: ==, !=, <, >, <=, >=
  • Logical: &&, ||, !
  • String literals: "hello", 'world'
  • Variable access: count, name
  • Basic property access: obj.property

Limitations

  • No complex objects or methods
  • No loops or conditionals in expressions
  • Limited string manipulation
  • No external libraries

Native-Java Data Model

Compile-time Java code generation with full type safety.

When to Use

  • Type safety required
  • Complex business logic
  • Performance critical applications
  • IDE support needed (autocomplete, refactoring)
  • Using Java collections and APIs

Example

xml
<scxml datamodel="native-java" initial="init">
    <datamodel>
        <data id="counter" type="int" expr="0"/>
        <data id="items" type="java.util.List&lt;String&gt;"
              expr="new java.util.ArrayList&lt;&gt;()"/>
        <data id="balance" type="double" expr="1000.0"/>
    </datamodel>

    <state id="init">
        <onentry>
            <script>
                // Direct Java code
                for (int i = 0; i &lt; 5; i++) {
                    items.add("Item " + i);
                }

                // Use Java APIs
                double interest = balance * 0.05;
                balance += interest;

                // Stream operations
                long count = items.stream()
                    .filter(s -> s.startsWith("Item"))
                    .count();
            </script>
        </onentry>
        <transition cond="items.size() &gt;= 5 &amp;&amp; balance &gt; 1000"
                    target="done"/>
    </state>

    <final id="done"/>
</scxml>

Features

  • Typed fields: <data id="count" type="Integer" expr="0"/>
  • Generics support: List<String>, Map<String, Object>
  • Direct Java code: Scripts embedded as Java in generated code
  • Full Java APIs: Use any Java library
  • Compile-time checks: Catch errors early
  • IDE support: Full autocomplete and refactoring

Generated Code

java
import com.scxmlgen.runtime.TranspiledStateMachine;

public final class MyStateMachine extends TranspiledStateMachine {
    // Native datamodel typed fields
    protected int counter;
    protected java.util.List<String> items;
    protected double balance;

    @Override
    protected void configureDataModel() {
        this.counter = 0;
        this.items = new java.util.ArrayList<>();
        this.balance = 1000.0;

        // Scripts embedded directly
        {
            for (int i = 0; i < 5; i++) {
                items.add("Item " + i);
            }
            // ...
        }
    }
}

XML Escaping for Generics

Use XML entities for generic types:

  • &lt; for <
  • &gt; for >
  • &amp; for &
xml
<data id="map" type="java.util.Map&lt;String, Integer&gt;"
      expr="new java.util.HashMap&lt;&gt;()"/>

Native-CSharp Data Model

Compile-time C# code generation with full type safety.

When to Use

  • Type safety required in C#/.NET applications
  • Complex business logic with C# collections
  • Performance critical applications
  • IDE support needed (autocomplete, refactoring)
  • Using .NET APIs and libraries

Example

xml
<scxml datamodel="native-csharp" initial="init">
    <datamodel>
        <data id="counter" type="int" expr="0"/>
        <data id="items" type="List&lt;string&gt;"
              expr="new List&lt;string&gt;()"/>
        <data id="balance" type="double" expr="1000.0"/>
    </datamodel>

    <state id="init">
        <onentry>
            <script>
                // Direct C# code
                for (int i = 0; i &lt; 5; i++) {
                    items.Add("Item " + i);
                }

                // Use .NET APIs
                double interest = balance * 0.05;
                balance += interest;

                // LINQ operations
                int count = items.Where(s => s.StartsWith("Item")).Count();
            </script>
        </onentry>
        <transition cond="items.Count &gt;= 5 &amp;&amp; balance &gt; 1000"
                    target="done"/>
    </state>

    <final id="done"/>
</scxml>

Features

  • Typed fields: <data id="count" type="int" expr="0"/>
  • Generics support: List<string>, Dictionary<string, object>
  • Direct C# code: Scripts embedded as C# in generated code
  • Full .NET APIs: Use any .NET library
  • Compile-time checks: Catch errors early
  • IDE support: Full autocomplete and refactoring

Generated Code

csharp
using ScxmlGen.Runtime;

public class MyStateMachine : TranspiledStateMachine
{
    // Native datamodel typed fields
    protected int counter;
    protected List<string> items;
    protected double balance;

    protected override void ConfigureDataModel()
    {
        this.counter = 0;
        this.items = new List<string>();
        this.balance = 1000.0;

        // Scripts embedded directly
        for (int i = 0; i < 5; i++) {
            items.Add("Item " + i);
        }
    }
}

ECMAScript Data Model

Full JavaScript runtime with Mozilla Rhino (Java), Jint (C#), or native (JavaScript).

When to Use

  • Dynamic typing needed
  • Complex scripting and algorithms
  • W3C SCXML compatibility required
  • Prototype-based programming
  • JSON data manipulation

Example

xml
<scxml datamodel="ecmascript" initial="start">
    <datamodel>
        <data id="user" expr="{name: 'Alice', age: 30, roles: []}"/>
        <data id="config" expr="{}"/>
    </datamodel>

    <state id="start">
        <onentry>
            <script>
                // JavaScript code
                user.roles.push('admin', 'editor');

                config.settings = {
                    theme: 'dark',
                    language: 'en'
                };

                // Functions
                function validateUser(u) {
                    return u.name && u.age >= 18;
                }

                var isValid = validateUser(user);
            </script>
            <log label="User" expr="JSON.stringify(user)"/>
        </onentry>
        <transition cond="user.roles.length &gt; 0" target="done"/>
    </state>

    <final id="done"/>
</scxml>

Features

  • Full ECMAScript 5.1+ support via Rhino/GraalJS (Java), Jint (C#), dukpy (Python), or native (JavaScript)
  • Dynamic objects and arrays
  • Functions and closures
  • JSON support
  • Prototype-based inheritance
  • W3C SCXML 100% compliant (Java, JS, C#, Python, C)

Dependencies

Java (Gradle):

gradle
dependencies {
    implementation 'org.mozilla:rhino:1.7.14'
}

C# (.NET):

xml
<PackageReference Include="Jint" Version="3.1.6" />

Python (pip):

bash
pip install dukpy>=0.3.0

XPath Data Model

XML data processing with XPath 2.0.

When to Use

  • Processing XML documents
  • XPath queries on hierarchical data
  • XML transformation workflows
  • W3C SCXML compatibility

Example

xml
<scxml datamodel="xpath" initial="start">
    <datamodel>
        <data id="cart">
            <cart xmlns="">
                <item id="1" price="29.99">Book</item>
                <item id="2" price="49.99">Headphones</item>
            </cart>
        </data>
    </datamodel>

    <state id="start">
        <onentry>
            <assign location="$cart/cart/item[1]/@price" expr="'24.99'"/>
            <log label="Total" expr="sum($cart//item/@price)"/>
        </onentry>
        <transition cond="count($cart//item) = 2" target="done"/>
    </state>

    <final id="done"/>
</scxml>

Features

  • XPath 2.0 expressions
  • XML document manipulation
  • Built-in XPath functions
  • W3C SCXML 87.8% compliant (159/181 tests) - Java only

Dependency

gradle
dependencies {
    implementation 'net.sf.saxon:Saxon-HE:12.3'
}

Known Limitations

  • HTTP I/O processor tests (optional W3C feature) - 22 tests
  • XPath 2.0 advanced features partially implemented

Null Data Model

No data model - pure state transitions.

When to Use

  • Simple state machines without data
  • Event-driven workflows
  • Protocol state machines
  • Minimal overhead needed

Example

xml
<scxml datamodel="null" initial="idle">
    <state id="idle">
        <transition event="start" target="running"/>
    </state>

    <state id="running">
        <transition event="stop" target="idle"/>
        <transition event="error" target="error"/>
    </state>

    <state id="error">
        <transition event="reset" target="idle"/>
    </state>
</scxml>

Features

  • No data variables
  • No expressions or conditions
  • Fastest execution
  • Minimal memory footprint
  • Event-based only

Migration Guide

From Simple to native-java

Before (Simple):

xml
<scxml datamodel="simple" initial="start">
    <datamodel>
        <data id="count" expr="0"/>
    </datamodel>
    <state id="start">
        <onentry>
            <assign location="count" expr="count + 1"/>
        </onentry>
    </state>
</scxml>

After (native-java):

xml
<scxml datamodel="native-java" initial="start">
    <datamodel>
        <data id="count" type="int" expr="0"/>
    </datamodel>
    <state id="start">
        <onentry>
            <assign location="count" expr="count + 1"/>
        </onentry>
    </state>
</scxml>

From ECMAScript to native-java

Before (ECMAScript):

xml
<scxml datamodel="ecmascript" initial="start">
    <datamodel>
        <data id="items" expr="[]"/>
    </datamodel>
    <state id="start">
        <onentry>
            <script>
                items.push("Alice");
                items.push("Bob");
            </script>
        </onentry>
    </state>
</scxml>

After (native-java):

xml
<scxml datamodel="native-java" initial="start">
    <datamodel>
        <data id="items" type="java.util.List&lt;String&gt;"
              expr="new java.util.ArrayList&lt;&gt;()"/>
    </datamodel>
    <state id="start">
        <onentry>
            <script>
                items.add("Alice");
                items.add("Bob");
            </script>
        </onentry>
    </state>
</scxml>

Default Data Model

If no datamodel attribute is specified, Simple is used by default:

xml
<scxml initial="start">  <!-- defaults to datamodel="simple" -->
    <datamodel>
        <data id="count" expr="0"/>
    </datamodel>
    <!-- ... -->
</scxml>

System Variables

All data models (except Null) provide system variables:

  • _event - Current event being processed
  • _sessionid - Unique session identifier
  • _name - State machine name
  • _ioprocessors - Available I/O processors

Example:

xml
<state id="processing">
    <onentry>
        <log label="Event" expr="_event.name"/>
        <log label="Session" expr="_sessionid"/>
    </onentry>
</state>

Performance Comparison

Benchmark results (operations per second):

Native:      1,000,000 ops/sec  (100%)
Simple:        800,000 ops/sec  ( 80%)
ECMAScript:    100,000 ops/sec  ( 10%)
XPath:          50,000 ops/sec  (  5%)
Null:        2,000,000 ops/sec  (200%)

Note: Actual performance varies by workload


Choosing a Data Model

Use Simple if:

  • Small state machine with basic data
  • Quick prototyping
  • No type safety needed

Use native-java if:

  • Type safety required
  • Complex Java logic
  • Performance critical
  • Using Java libraries

Use native-js if:

  • JavaScript runtime interpreter
  • Native JS expressions in browser/Node.js
  • Performance critical in JS environment

Use native-csharp if:

  • Type safety required in C#/.NET
  • Complex C# logic with LINQ
  • Performance critical in .NET environment
  • Using .NET libraries

Use native-python if:

  • Type safety required in Python
  • Type hints for IDE support
  • Performance critical in Python environment
  • Using Python libraries

Use native-go if:

  • Type safety required in Go
  • Compile-time type checking
  • Performance critical in Go environment
  • Concurrency with goroutines
  • Using Go libraries

Use ECMAScript if:

  • Dynamic typing needed
  • W3C compliance required
  • JavaScript expertise available

Use XPath if:

  • Processing XML documents
  • XPath queries needed
  • XML workflows

Use Null if:

  • No data needed
  • Pure event-driven
  • Minimal overhead required

Additional Resources