Schema object model

The decoder builds an in-memory schema model from XML. This model keeps message definitions, type definitions, offsets, sizes, and semantic traits.

Use this model when you need:

  • dynamic routing by template ID;
  • metadata-driven validation;
  • diagnostics and schema introspection;
  • virtual field lookup for composite members.

Main entry points

Top-level objects:

Build lifecycle

The parser reads XML and builds MessageSchema with a graph of UnifiedMetaData nodes.

Symbolic XML type names are resolved into direct links between metadata nodes:

Then the model computes effective offsets and sizes, inserts padding when required, resolves header/value metadata, builds virtual fields, and validates schema consistency.

MessageSchema structure

MessageSchema stores:

Practical notes:

UnifiedMetaData model

UnifiedMetaData is used for messages, fields, groups, types, enum values, set choices, and virtual fields.

Core identity and traits:

Kind categories

Important Kind values:

Kind.isTypeDefinition() is useful to separate dictionary entries from message structure nodes.

Parent and containment links

Navigation links:

SBE standard defines fieldset order:

  • fixed fields first;
  • then groups;
  • then var-data.

If this order is violated, parsing fails with schema validation error.

Chained resolution behavior

Many getters in UnifiedMetaData are effectively chained through getResolvedUnderlayingType(). This means metadata can be inherited from referenced type when not set directly on field.

Typical chained getters:

This behavior is central for fields that reference user-defined types.

Offsets, sizes, and padding

Key offset/size APIs:

resolveOffsets() computes effective layout:

  • validates explicit offsets and block length;
  • assigns calculatedOffset;
  • inserts padding when needed;
  • recurses into nested composites/groups/data.

Header-size helper:

Type recognition and composite classification

Schema post-processing classifies composites into WellKnownComposite:

Related checks:

Field lookup and query patterns

Most useful lookup methods:

Behavior details:

Virtual fields and message header access

Virtual fields represent composite members as flat field IDs. They are useful when you need direct IFieldSet access without custom composite interfaces.

Flow:

  1. Build MessageSchema.
  2. Prepare field path (message -> field -> member, or "*" for message header).
  3. Call queryVirtualFieldId(String[]).
  4. Use resulting ID with regular getters/setters.

Important points:

  • virtual IDs are negative;
  • IDs are unique inside schema runtime;
  • 0 means “not found”.

Java type mapping hints

UnifiedMetaData provides type mapping helpers:

Examples:

  • known composites map to wrapper classes (scaled-decimal wrapper, MonthYear, SbeTimestamp);
  • char arrays map to Java string values.

Cross-links

Traversal examples

Traverse messages, fields, groups, and virtual fields

    public static void traverseMessagesFieldsGroupsAndVirtuals() throws LicenseException {
        MessageSchema schema = new MessageSchema(ParseUtil.parse(new File("SbeXmlTemplate.xml")));

        for (Map.Entry<Integer, UnifiedMetaData> messageEntry : schema.getMessages().entrySet()) {
            Integer templateId = messageEntry.getKey();
            UnifiedMetaData message = messageEntry.getValue();

            System.out.println("Message: " + message.getName() + " (templateId=" + templateId + ")");
            printFieldSet(message, "  ");
        }
    }

    private static void printFieldSet(UnifiedMetaData fieldSet, String indent) {
        for (UnifiedMetaData field : fieldSet.getFixedSizeBlock()) {
            String fieldName = field.getName();
            Integer fieldTag = field.getTag();
            if (fieldTag != null && fieldTag < 0) {
                // Internal virtual service field (for example, synthetic message-header field).
                continue;
            }

            System.out.println(indent + "Field: " + fieldName + " (tag=" + fieldTag + ")");

            UnifiedMetaData[] virtualFields = field.getVirtualFields();
            if (virtualFields != null && virtualFields.length > 0) {
                for (UnifiedMetaData virtualField : virtualFields) {
                    UnifiedMetaData baseField = virtualField.getResolvedUnderlayingType();
                    System.out.println(
                            indent + "  Virtual: " + baseField.getInheritedName() + " (tag=" + virtualField.getTag() + ")");
                }
            }
        }

        for (UnifiedMetaData group : fieldSet.getGroups()) {
            System.out.println(indent + "Group: " + group.getName() + " (tag=" + group.getTag() + ")");
            printFieldSet(group, indent + "  ");
        }
    }

Find deepest type for selected field

The snippet walks through getResolvedUnderlayingType() chain until the final node.

    public static UnifiedMetaData findDeepestType(
            MessageSchema schema,
            String messageName,
            String fieldName) {

        UnifiedMetaData message = schema.getMessagesByNames().get(messageName);
        if (message == null) {
            return null;
        }

        UnifiedMetaData field = message.findField(fieldName);
        if (field == null) {
            return null;
        }

        UnifiedMetaData deepest = field;
        while (deepest.getResolvedUnderlayingType() != null) {
            deepest = deepest.getResolvedUnderlayingType();
        }

        return deepest;
    }

Build list of fields that use custom composites

The snippet collects fields where:

    public static List<String> listCustomCompositeFields(MessageSchema schema) {
        List<String> customCompositeFields = new ArrayList<>();

        for (Map.Entry<Integer, UnifiedMetaData> messageEntry : schema.getMessages().entrySet()) {
            UnifiedMetaData message = messageEntry.getValue();
            collectCustomComposites(message, message.getName(), customCompositeFields);
        }

        return customCompositeFields;
    }

    private static void collectCustomComposites(
            UnifiedMetaData fieldSet,
            String pathPrefix,
            List<String> destination) {

        for (UnifiedMetaData field : fieldSet.getFixedSizeBlock()) {
            Integer tag = field.getTag();
            if (tag != null && tag < 0) {
                continue;
            }

            if (field.isComposite() && field.getWellKnownComposite() == null) {
                destination.add(pathPrefix + "/" + field.getName() + " (tag=" + field.getTag() + ")");
            }
        }

        for (UnifiedMetaData group : fieldSet.getGroups()) {
            collectCustomComposites(group, pathPrefix + "/" + group.getName(), destination);
        }
    }

Traverse fields inside selected custom composite

The snippet resolves message field, validates it as custom composite, and walks all composite members (including nested composite members).

    public static void traverseCustomCompositeMembers(
            MessageSchema schema,
            String messageName,
            String fieldName) {

        UnifiedMetaData message = schema.getMessagesByNames().get(messageName);
        if (message == null) {
            throw new IllegalArgumentException("Message not found: " + messageName);
        }

        UnifiedMetaData field = message.findField(fieldName);
        if (field == null) {
            throw new IllegalArgumentException("Field not found: " + fieldName);
        }

        if (!field.isComposite() || field.getWellKnownComposite() != null) {
            throw new IllegalArgumentException("Field is not a custom composite: " + fieldName);
        }

        UnifiedMetaData composite = field.getCompositeMembersContainer();
        if (composite == null || composite.getCompositeMembers() == null) {
            return;
        }

        System.out.println("Custom composite: " + messageName + "/" + fieldName);
        printCompositeMembers(composite, "  ");
    }

    private static void printCompositeMembers(UnifiedMetaData composite, String indent) {
        for (UnifiedMetaData member : composite.getCompositeMembers()) {
            System.out.println(
                    indent + member.getName()
                            + " (kind=" + member.getKind()
                            + ", offset=" + member.getCalculatedOffset()
                            + ", size=" + member.getFixedSize() + ")");

            if (member.isComposite()
                    && member.getCompositeMembersContainer() != null
                    && member.getCompositeMembersContainer().getCompositeMembers() != null) {
                printCompositeMembers(member.getCompositeMembersContainer(), indent + "  ");
            }
        }
    }