generic quickfix.Group, and writes them to the output target.

Java

import java.util.Iterator;
import quickfix.DataDictionary;
import quickfix.Field;
import quickfix.FieldMap;
import quickfix.FieldNotFound;
import quickfix.Group;
import quickfix.StringField;

public class DynamicFixPassThrough {

    /**
     * Recursively copies all top-level fields and repeating groups from input to output
     * without relying on generated class types or hardcoded tag constants.
     */
    public static void copyAllFieldsAndGroups(FieldMap input, FieldMap output, DataDictionary dictionary, String msgType) {
        
        // 1. Copy all direct top-level fields of this map/group
        Iterator<Field<?>> iterator = input.iterator();
        while (iterator.hasNext()) {
            Field<?> field = iterator.next();
            int tag = field.getTag();

            // Check if this tag represents a repeating group count tag
            if (dictionary.isGroup(msgType, tag)) {
                // Handle as repeating group (Step 2)
                copyGroupDynamically(input, output, tag, dictionary, msgType);
            } else {
                // Standard non-group field copy
                output.setField(new StringField(tag, field.getObject().toString()));
            }
        }
    }

    private static void copyGroupDynamically(FieldMap input, FieldMap output, int groupCountTag, DataDictionary dictionary, String msgType) {
        try {
            int groupCount = input.getInt(groupCountTag);
            if (groupCount <= 0) {
                return;
            }

            // Look up the group delimiter (first tag) from DataDictionary
            DataDictionary.GroupInfo groupInfo = dictionary.getGroup(msgType, groupCountTag);
            if (groupInfo == null) {
                return; // Tag is registered as a group but group definition is missing
            }
            int delimiterTag = groupInfo.getDelimiterField();

            // Iterate over all group instances
            for (int i = 1; i <= groupCount; i++) {
                // Instantiate generic Group with (counterTag, delimiterTag)
                Group inputGroup = new Group(groupCountTag, delimiterTag);
                input.getGroup(i, inputGroup);

                Group outputGroup = new Group(groupCountTag, delimiterTag);

                // RECURSION: Recursively copy fields & nested sub-groups within this group entry
                copyAllFieldsAndGroups(inputGroup, outputGroup, groupInfo.getDataDictionary(), msgType);

                // Add populated group instance to target map
                output.addGroup(outputGroup);
            }
        } catch (FieldNotFound e) {
            // Group count field exists, but group entries couldn't be extracted
        }
    }
}

How to Use It in Your Code

To execute this, retrieve the active DataDictionary from your QuickFIX/J Session/Application context:

Java

import quickfix.DataDictionary;
import quickfix.DataDictionaryProvider;
import quickfix.fix50.ExecutionReport;

public void processExecutionReport(ExecutionReport inputMsg, ExecutionReport outputMsg, DataDictionaryProvider dictProvider) {
    // 1. Get the DataDictionary for FIX 5.0 (or your active session dictionary)
    DataDictionary dictionary = dictProvider.getSessionDataDictionary("FIX.5.0"); 
    // Or from Message: dictionary = dictProvider.getAppDataDictionary(inputMsg.getHeader().getString(8));

    String msgType = "8"; // ExecutionReport

    // 2. Run generic dynamic copy for all fields and repeating groups
    DynamicFixPassThrough.copyAllFieldsAndGroups(inputMsg, outputMsg, dictionary, msgType);
}

What if a Group Tag is NOT in the Dictionary?

If you receive custom repeating groups that are completely unknown to QuickFIX/J’s DataDictionary (meaning dictionary.isGroup(msgType, tag) returns false), QuickFIX/J will not parse those fields into nested groups when the raw FIX string is first parsed into the Message object.

Instead, QuickFIX/J parses unknown repeating group fields into the root Message flat list as standard top-level fields. In that scenario, standard top-level StringField copying (Option 1 from the first response) will automatically pass them along raw without needing special group logic!

Step 1 Step 2 Step 3