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:
- IMessageSchema is the general interface.
- MessageSchema is the runtime implementation.
- UnifiedMetaData is the unified node type for most schema elements.
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:
- source names stay available via
getUnderlayingType()andgetUnderlayingPrimitiveTypeName(); - resolved links are available via
getResolvedUnderlayingType()andgetResolvedDimensionType().
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:
- schema metadata (
getPackage(),getId(),getVersion(),getSemanticVersion(),isLittleEndian()); - type map by name (
getType(String)); - message map by template ID (
getMessages()); - resolved header metadata (
getHeaderType(),getResolvedHeaderType()); - framing header specification (
getFramingHeaderSpecification(),getFrameHeaderSize()).
Practical notes:
getMessages()returns messages by template ID.- Concrete MessageSchema also keeps messages by name (
getMessagesByNames()). queryVirtualFieldId(String[])resolves virtual IDs for composite members and message-header custom fields.
UnifiedMetaData model
UnifiedMetaData is used for messages, fields, groups, types, enum values, set choices, and virtual fields.
Core identity and traits:
getKind()defines node category.getTag(),getName(),getDescription()provide identity.getPresence(),getSinceVersion(),getSemanticType()provide behavior hints.getPrimitiveType()andgetResolvedUnderlayingType()describe encoding chain.
Kind categories
Important Kind values:
- Fieldset nodes:
FixMessageType,FixGroup. - Field nodes:
FixField,VarData. - Type nodes:
SimpleType,CompositeType,EnumType,SetType,PrimitiveType. - Type members:
MemberDataType,RefType,ValidValue,Choice. - Runtime internals:
VirtualField,Padding,DefaultMessageType.
Kind.isTypeDefinition() is useful to separate dictionary entries from message structure nodes.
Parent and containment links
Navigation links:
getParent()returns logical container.getFixedSizeBlock()returns fixed fields of message/group.getGroups()returns repeating groups.getData()returns var-data fields.getCompositeMembers()returns composite members.getValues()returns enum valid values or set choices.
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:
getPrimitiveType()getLength()getPresence()getNullValue()(with specific array/null semantics)getMinValue()andgetMaxValue()getSemanticType()andgetCharacterEncoding()
This behavior is central for fields that reference user-defined types.
Offsets, sizes, and padding
Key offset/size APIs:
getExplicitOffset()andgetExplicitAlignment()getCalculatedOffset()getPaddingBefore()andgetPaddingAfter()getFixedSize()getOffsetInFieldSet(int)
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:
getFieldSetHeaderSize()returnsSOFH + messageHeadersize for messages.- for groups it currently returns
0.
Type recognition and composite classification
Schema post-processing classifies composites into WellKnownComposite:
Related checks:
Field lookup and query patterns
Most useful lookup methods:
findField(String)andfindField(int)on message/group metadata.findCompositeMember(String)on composite metadata.findOwnValue(String)on enum/set metadata.
Behavior details:
findField(int)can resolve virtual tags (negative IDs).- virtual tags are available via
getVirtualFields().
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:
- Build MessageSchema.
- Prepare field path (message -> field -> member, or
"*"for message header). - Call
queryVirtualFieldId(String[]). - Use resulting ID with regular getters/setters.
Important points:
- virtual IDs are negative;
- IDs are unique inside schema runtime;
0means “not found”.
Java type mapping hints
UnifiedMetaData provides type mapping helpers:
Examples:
- known composites map to wrapper classes (scaled-decimal wrapper, MonthYear, SbeTimestamp);
chararrays map to Java string values.
Cross-links
- Encoding/decoding flow: Message guide
- Composite behavior: Composite data
- Header handling: Framing and message headers
- Runtime checks: Configuring
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:
- field is composite;
- composite is not recognized as WellKnownComposite.
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 + " ");
}
}
}
Java SBE Decoder