# Creating Complex UDT Definitions with DataTypes with Java SDK

**URL:** <https://forum.inductiveautomation.com/t/creating-complex-udt-definitions-with-datatypes-with-java-sdk/113496>\
**Category:** Module Development\
**Tags:** ignition83\
**Created:** [January 22, 2026, 8:17pm UTC](https://forum.inductiveautomation.com/t/creating-complex-udt-definitions-with-datatypes-with-java-sdk/113496 "2026-01-22T20:17:14Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![rhys.nickerson](https://avatars.discourse-cdn.com/v4/letter/r/ea666f/32.png) [@rhys.nickerson](https://forum.inductiveautomation.com/u/rhys.nickerson)\
**Post date:** [January 22, 2026, 8:17pm UTC](https://forum.inductiveautomation.com/t/creating-complex-udt-definitions-with-datatypes-with-java-sdk/113496/1 "2026-01-22T20:17:14Z")

</div>

Hi, I am looking to automatically create UDT Definitions via the Java SDK in Ignition 8.3 in a `ManagedTagProvider`. (io.ia.sdk.modl version 0.4.0 using a GatewayHook)

I instantiate my `ManagedTagProvider` with the `TagProviderMeta.FLAG_PROVIDES_COMPLEX_TYPES` flag set to true, and instantiate my UDT

Definition with the following:

```python
        provider = manager.getOrCreateManagedProvider(getProviderConfig());

        BasicTagConfiguration model = new BasicTagConfiguration();
        model.setName("MyUDT"); //setName always seems superfluous as configureTag sets name based on path
        model.setType(TagObjectType.UdtType);

        BasicTagConfiguration subFolder = new BasicTagConfiguration();
        subFolder.setName("SubFolder");
        subFolder.setType(TagObjectType.Folder);

        BasicTagConfiguration subTag1 = new BasicTagConfiguration();
        subTag1.setName("SubTag1");
        subTag1.setType(TagObjectType.AtomicTag);
        subTag1.set(WellKnownTagProps.DataType, DataType.Float4);
        subTag1.set(WellKnownTagProps.TagGroup, "Default");

        BasicTagConfiguration subTag2 = new BasicTagConfiguration();
        subTag2.setName("SubTag2");
        subTag2.setType(TagObjectType.AtomicTag);
        subTag2.set(WellKnownTagProps.DataType, DataType.Float8);
        subTag2.set(WellKnownTagProps.TagGroup, "Default");

        model.addChild(subFolder);
        subFolder.addChild(subTag1); //add child seems superfluous, based on path

        provider.configureTag("MyUDT", model);
        provider.configureTag("_types_/MyUDT/SubFolder", subFolder);
        provider.configureTag("_types_/MyUDT/SubFolder/SubTag1", subTag1);
        provider.configureTag("_types_/MyUDT/SubFolder/SubTag2", subTag2);

```

It does successfully create the hierarchy as seen below:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/2/122d29e17eb9a250f595a0b4e00ce17f66b588e6.png)  
However the DataType field is missing from the tag properties

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/9/e/9e8283cbed86e97fbdd87651ffa5d472a3cd1faf.png)  
But it is populated into a custom `prg` property.

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/5/a/5a900acda52b1e69fdcdb6862c4b7c3abc98912c.png)

I have also tried various other methods of instantiating tags, notably:  
`TagConfigurationBuilder`, `TagConfigurationModel`, `UdtConfigDefinition`  
which all produced similar results.

Any help or guidance on the correct process would be greatly appreciated. Thank you!

---

<div class="post-metadata">

**Author:** ![rhys.nickerson](https://avatars.discourse-cdn.com/v4/letter/r/ea666f/32.png) [@rhys.nickerson](https://forum.inductiveautomation.com/u/rhys.nickerson)\
**Post date:** [January 22, 2026, 9:47pm UTC](https://forum.inductiveautomation.com/t/creating-complex-udt-definitions-with-datatypes-with-java-sdk/113496/2 "2026-01-22T21:47:08Z")

</div>

To clarify, I have also set `hasDataTypes` to `true`in my `ManagedTagProviderConfiguration`and the issue persists.

---

<div class="post-metadata">

**Author:** ![mdevos](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mdevos/32/82490_2.png) [@mdevos](https://forum.inductiveautomation.com/u/mdevos)\
**Post date:** [February 5, 2026, 6:50am UTC](https://forum.inductiveautomation.com/t/creating-complex-udt-definitions-with-datatypes-with-java-sdk/113496/3 "2026-02-05T06:50:56Z")

</div>

This is somewhat of a perennial topic on this forum, so I'm hijacking your thread in the hopes that I can save future travelers some of their time.

## Configuring `ManagedTagProvider` for UDT support

Ensure that our `ManagedTagProviderConfiguration` is configured with (at least) the following properties.

```java
var mtpConfig = 
    new ManagedTagProviderConfiguration
        .Builder("mtp") // name of our provider
        .hasDataTypes(true) // enables FLAG_PROVIDES_COMPLEX_TYPES
        .build();

var tagManager = gatewayContext.getTagManager();
ManagedTagProvider mtp = tagManager.getOrCreateManagedProvider(mtpConfig)

```

## Declaring UDT members

This step is concerned exclusively with the declaration of UDT members. Do not attempt to declare relationships between members until all members are instantiated. This way, there is no need to worry about the sequencing of declaration / instantiation.

```java
// configuration for a UDT definition tag MyUdt
var myUdtPath = TagPathParser.parse("[mtp]_types_/MyUdt");
var myUdtConfig = BasicTagConfiguration.createNew(myUdtPath);
myUdtConfig.setType(TagObjectType.UdtType);

// configuration for a memory Float4 tag at MyUdt/SubTag1
var subTag1Path = TagPathParser.parse("[mtp]_types_/MyUdt/SubTag1");
var subTag1Config = BasicTagConfiguration.createNew(subTag1Path);
subTag1Config.setType(TagObjectType.AtomicTag);
subTag1Config.set(WellKnownTagProps.DataType, DataType.Float4);
subTag1Config.set(WellKnownTagProps.TagGroup, "Default");
subTag1Config.set(WellKnownTagProps.ValueSource, WellKnownTagProps.MEMORY_TAG_TYPE);
subTag1Config.set(WellKnownTagProps.Value, BasicQualifiedValue.fromObject(0)); // default value

// configuration for a Folder tag at MyUdt/SubFolder
var subFolderPath = TagPathParser.parse("[mtp]_types_/MyUdt/SubFolder");
var subFolderConfig = BasicTagConfiguration.createNew(subFolderPath);
subFolderConfig.setType(TagObjectType.Folder);

// configuration for an OPC Int4 tag at MyUdt/SubFolder/SubTag2
var subTag2Path = TagPathParser.parse("[mtp]_types_/MyUdt/SubFolder/SubTag2");
var subTag2Config = BasicTagConfiguration.createNew(subTag2Path);
subTag2Config.setType(TagObjectType.AtomicTag);
subTag2Config.set(WellKnownTagProps.DataType, DataType.String);
subTag2Config.set(WellKnownTagProps.TagGroup, "Default");
subTag2Config.set(WellKnownTagProps.ValueSource, TagManager.DATA_SOURCE_OPC);
subTag2Config.set(WellKnownTagProps.Value, BasicQualifiedValue.fromObject(0)); // default value
subTag2Config.set(TagProp.OPCServer, "Ignition OPC UA Server");
subTag2Config.set(TagProp.OPCItemPath, "i=2259"); // i=2259, gateway status code

```

#### Guidance & Notes

- Use the `BasicTagConfiguration.createNew()` function in place of the `BasicTagConfiguration()` constructor.
- Ensure that all atomic tags declare each of the following property types
  - `WellKnownTagProps.DataType`
  - `WellKnownTagProps.TagGroup`
  - `WellKnownTagProps.ValueSource`
  - `WellKnownTagProps.Value` (if you want to establish default values)

- I have had extensive issues with `TagConfigurationBuilder`, and thus I do not recommend using it at all. This was not for a lack of effort, as it is sorely missed. I am highly invested in seeing this changed, so I will continue to investigate and share my findings in a follow-up post.
- @rhys.nickerson's observations about the [`setName`](https://files.inductiveautomation.com/sdk/javadoc/ignition83/8.3.3/com/inductiveautomation/ignition/common/tags/config/BasicTagConfiguration.html#setName(java.lang.String)) and [`addChild`](https://files.inductiveautomation.com/sdk/javadoc/ignition83/8.3.3/com/inductiveautomation/ignition/common/tags/config/BasicTagConfiguration.html#addChild(com.inductiveautomation.ignition.common.tags.config.TagConfiguration)) functions are correct - they're just helper functions which manipulate the caller's internal `TagPath` member. They don't perform linking or have any side-effects. Use these with caution, as they will make "invisible" changes to your tag paths which can be difficult to keep track of.

# Linking, saving, and the trouble with `configureTag()`

The [`managed-tag-provider`](https://github.com/inductiveautomation/ignition-sdk-examples/blob/aafc6c3ce2c93841f2856c8e0124c7cec33167ea/managed-tag-provider/managed-tag-provider-gateway/src/main/java/com/inductiveautomation/ignition/examples/mtp/ManagedProviderGatewayHook.java#L60) sample demonstrates the use of [`ManagedTagProvider::configureTag()`](https://files.inductiveautomation.com/sdk/javadoc/ignition83/8.3.3/com/inductiveautomation/ignition/gateway/tags/managed/ManagedTagProvider.html#configureTag(java.lang.String,com.inductiveautomation.ignition.common.config.BoundPropertySet)) for transforming `TagConfiguration` objects into actual tags. This works flawlessly when restricted to atomic tags, but becomes troublesome once UDTs are introduced.

`configureTag()`'s internal handling of UDT definitions/instances assumes that the UDT's tag hierarchy is explicitly declared through the use of the [`EncodingProps.Tags` (`"tags"`)](https://files.inductiveautomation.com/sdk/javadoc/ignition83/8.3.3/com/inductiveautomation/ignition/common/tags/config/properties/EncodingProps.html#Tags) tag property. Specifically, it is assumed that any tag with children provides a `List<TagPath>` object with entries for all immediate children via the `"tags"` tag property. If this sounds familiar, it's because this is how JSON tag imports/exports are structured. The internals behind this process borrow a significant amount of code from that subsystem, and as such, the most SDK-compliant way to perform this operation is to modify the structure of our configuration data to match.

By hand-populating the `"tags"` properties for any tags with children, we are able to provide `configureTag()` and its callees with the information they are expecting. From there, we can load a full UDT definition, including all of its descendant tags, by passing just the UDT definition's `TagConfiguration` object into `configureTag()`. The descendant tags will be aggregated during the execution of `configureTag()` and initialized in the gateway.

Here's what that looks like for the previously declared components:

```java
// populate "tags" property for tags with children
subFolderConfig.set(EncodingProps.Tags, List.of(subTag2Config));
myUdtConfig.set(EncodingProps.Tags, List.of(subFolderConfig, subTag1Config));
mtp.configureTag("[mtp]_types_/MyUdt", myUdtConfig);

```

# Creating UDT instances

Once the UDT definitions are squared away, creating UDT instances is very straightforward.

```java
// declare and bootstrap an instance of MyUdt called MyUdtInstance
TagPath myUdtInstancePath = TagPathParser.parse("[mtp]MyUdtInstance");
TagConfiguration myUdtInstanceConfig = BasicTagConfiguration.createNew(myUdtInstancePath);
myUdtInstanceConfig.setType(TagObjectType.UdtInstance);
myUdtInstanceConfig.set(WellKnownTagProps.TypeId, "MyUdt");
mtp.configureTag("[mtp]MyUdtInstance", myUdtInstanceConfig);

```

# Complete, working example (as of Ignition 8.3.2)

```java
var mtpConfig = 
   new ManagedTagProviderConfiguration
       .Builder("mtp") // name of our provider
       .hasDataTypes(true) // enables FLAG_PROVIDES_COMPLEX_TYPES
       .build();

var tagManager = gatewayContext.getTagManager();
ManagedTagProvider mtp = tagManager.getOrCreateManagedProvider(mtpConfig)

// configuration for a UDT definition tag MyUdt
var myUdtPath = TagPathParser.parse("[mtp]_types_/MyUdt");
var myUdtConfig = BasicTagConfiguration.createNew(myUdtPath);
myUdtConfig.setType(TagObjectType.UdtType);

// configuration for a memory Float4 tag at MyUdt/SubTag1
var subTag1Path = TagPathParser.parse("[mtp]_types_/MyUdt/SubTag1");
var subTag1Config = BasicTagConfiguration.createNew(subTag1Path);
subTag1Config.setType(TagObjectType.AtomicTag);
subTag1Config.set(WellKnownTagProps.DataType, DataType.Float4);
subTag1Config.set(WellKnownTagProps.TagGroup, "Default");
subTag1Config.set(WellKnownTagProps.ValueSource, WellKnownTagProps.MEMORY_TAG_TYPE);
subTag1Config.set(WellKnownTagProps.Value, BasicQualifiedValue.fromObject(0)); // default value

// configuration for a Folder tag at MyUdt/SubFolder
var subFolderPath = TagPathParser.parse("[mtp]_types_/MyUdt/SubFolder");
var subFolderConfig = BasicTagConfiguration.createNew(subFolderPath);
subFolderConfig.setType(TagObjectType.Folder);

// configuration for an OPC Int4 tag at MyUdt/SubFolder/SubTag2
var subTag2Path = TagPathParser.parse("[mtp]_types_/MyUdt/SubFolder/SubTag2");
var subTag2Config = BasicTagConfiguration.createNew(subTag2Path);
subTag2Config.setType(TagObjectType.AtomicTag);
subTag2Config.set(WellKnownTagProps.DataType, DataType.String);
subTag2Config.set(WellKnownTagProps.TagGroup, "Default");
subTag2Config.set(WellKnownTagProps.ValueSource, TagManager.DATA_SOURCE_OPC);
subTag2Config.set(TagProp.OPCServer, "Ignition OPC UA Server");
subTag2Config.set(TagProp.OPCItemPath, "i=2259"); // i=2259, gateway status code

// populate "tags" property for tags with children
subFolderConfig.set(EncodingProps.Tags, List.of(subTag1Config));
myUdtConfig.set(EncodingProps.Tags, List.of(subFolderConfig, subTag2Config));
mtp.configureTag("[mtp]_types_/MyUdt", myUdtConfig);

// declare and bootstrap an instance of MyUdt called MyUdtInstance
TagPath myUdtInstancePath = TagPathParser.parse("[mtp]MyUdtInstance");
TagConfiguration myUdtInstanceConfig = BasicTagConfiguration.createNew(myUdtInstancePath);
myUdtInstanceConfig.setType(TagObjectType.UdtInstance);
myUdtInstanceConfig.set(WellKnownTagProps.TypeId, "MyUdt");
mtp.configureTag("[mtp]MyUdtInstance", myUdtInstanceConfig);

```

---

<div class="post-metadata">

**Author:** ![rhys.nickerson](https://avatars.discourse-cdn.com/v4/letter/r/ea666f/32.png) [@rhys.nickerson](https://forum.inductiveautomation.com/u/rhys.nickerson)\
**Post date:** [February 5, 2026, 8:06pm UTC](https://forum.inductiveautomation.com/t/creating-complex-udt-definitions-with-datatypes-with-java-sdk/113496/4 "2026-02-05T20:06:02Z")

</div>

Fantastic write-up, thank you so much.

---

<div class="post-metadata">

**Author:** ![David\_Stone](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/david_stone/32/79749_2.png) [@David\_Stone](https://forum.inductiveautomation.com/u/David_Stone)\
**Post date:** [February 5, 2026, 9:03pm UTC](https://forum.inductiveautomation.com/t/creating-complex-udt-definitions-with-datatypes-with-java-sdk/113496/5 "2026-02-05T21:03:11Z")

</div>

I’m reading this and wanting to know more about your MTP module…
