Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ public static void Render(MTConnectModel mtconnectModel, string outputPath)

case "Devices.Component": ((ClassModel)template).IsPartial = true; break;
case "Devices.Composition": ((ClassModel)template).IsPartial = true; break;
case "Devices.Configurations.Configuration": ((ClassModel)template).IsPartial = true; break;
case "Devices.DataItem": ((ClassModel)template).IsPartial = true; break;
case "Devices.AbstractDataItemRelationship": ((ClassModel)template).IsPartial = true; break;
case "Devices.References.Reference": ((ClassModel)template).IsPartial = true; break;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) 2026 TrakHound Inc., All Rights Reserved.
// TrakHound Inc. licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Xml.Linq;

namespace MTConnect.Devices.Configurations
{
/// <summary>
/// Hand-authored partial extension of the generated <see cref="Configuration"/>
/// concrete class that carries the vendor-extension collection declared on
/// <see cref="IConfiguration.VendorExtensions"/>. See the interface's remarks
/// for the MTConnect v2.7 XSD citation that underpins the semantics.
/// </summary>
public partial class Configuration
{
/// <inheritdoc cref="IConfiguration.VendorExtensions"/>
public IEnumerable<XElement> VendorExtensions { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace MTConnect.Devices.Configurations
/// <summary>
/// Technical information about an entity describing its physical layout, functional characteristics, and relationships with other entities.
/// </summary>
public class Configuration : IConfiguration
public partial class Configuration : IConfiguration
{
/// <summary>
/// The description of this type as defined by the MTConnect Standard.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2026 TrakHound Inc., All Rights Reserved.
// TrakHound Inc. licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Xml.Linq;

namespace MTConnect.Devices.Configurations
{
/// <summary>
/// Hand-authored partial extension of the generated <see cref="IConfiguration"/>
/// contract that adds the vendor-extension surface. The MTConnect Standard
/// itself defines vendor extension of a component's Configuration through the
/// XSD substitution group <c>AbstractConfiguration</c> — see
/// <c>MTConnectDevices_2.7.xsd</c> where <c>ComponentConfigurationType</c>
/// declares <c>&lt;xs:element ref="AbstractConfiguration" minOccurs="1"
/// maxOccurs="unbounded"/&gt;</c>, and every standard child (SensorConfiguration,
/// Specifications, Relationships, CoordinateSystems, Motion, SolidModel,
/// ImageFiles, PowerSources) is declared with
/// <c>substitutionGroup='AbstractConfiguration'</c>. A vendor publishes their
/// own XSD declaring a vendor-namespaced element that likewise substitutes for
/// <c>AbstractConfiguration</c>, and the operator supplies fully-formed
/// instances of that element through this collection.
/// </summary>
public partial interface IConfiguration
{
/// <summary>
/// Vendor-extension elements carried inside the component's
/// <c>&lt;Configuration&gt;</c>. Each entry MUST be a fully-formed,
/// vendor-namespaced XML element that a vendor XSD declares as a
/// substitution of the standard <c>AbstractConfiguration</c> abstract
/// element (see class-level remarks for the MTConnect XSD citation).
/// The MTConnect.NET XML formatter writes each element verbatim inside
/// the <c>&lt;Configuration&gt;</c> sequence, alongside any standard
/// children present on this instance; the deserializer captures any
/// child element it does not recognize as a standard configuration
/// child and adds it here. Strict XSD validation of the emitted
/// document is the caller's responsibility and requires the vendor XSD
/// to be loaded alongside the MTConnect schemas.
/// </summary>
IEnumerable<XElement> VendorExtensions { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace MTConnect.Devices.Configurations
/// <summary>
/// Technical information about an entity describing its physical layout, functional characteristics, and relationships with other entities.
/// </summary>
public interface IConfiguration
public partial interface IConfiguration
{
/// <summary>
/// Reference system that associates a unique set of n parameters with each point in an n-dimensional space. ISO 10303-218:2004
Expand Down
76 changes: 76 additions & 0 deletions libraries/MTConnect.NET-XML/Devices/XmlConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

using MTConnect.Devices.Configurations;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace MTConnect.Devices.Xml
Expand All @@ -15,6 +18,18 @@ namespace MTConnect.Devices.Xml
/// and converts to and from the strongly-typed <see cref="Configuration"/>
/// model.
/// </summary>
/// <remarks>
/// Any child element that is neither a standard configuration child nor a
/// namespaced vendor extension declared on this surrogate is captured
/// into <see cref="VendorExtensions"/> as a raw <see cref="XmlElement"/>
/// via <see cref="XmlAnyElementAttribute"/>, then projected onto
/// <see cref="IConfiguration.VendorExtensions"/> as an
/// <see cref="XElement"/>. This mirrors the MTConnect XSD's
/// <c>ComponentConfigurationType</c> which permits any element that
/// substitutes for the abstract <c>AbstractConfiguration</c> element —
/// i.e. any element a vendor XSD has declared with
/// <c>substitutionGroup='AbstractConfiguration'</c>.
/// </remarks>
[XmlRoot("Configuration")]
public class XmlConfiguration
{
Expand Down Expand Up @@ -61,6 +76,16 @@ public class XmlConfiguration
[XmlArrayItem("ProcessSpecification", typeof(XmlProcessSpecification))]
public List<XmlAbstractSpecification> Specifications { get; set; }

/// <summary>
/// Vendor-extension child elements captured verbatim from the
/// <c>&lt;Configuration&gt;</c> element by <see cref="XmlAnyElementAttribute"/>
/// — every child not bound to a strongly-typed slot above lands here.
/// See <see cref="IConfiguration.VendorExtensions"/> for the standard
/// citation.
/// </summary>
[XmlAnyElement]
public XmlElement[] VendorExtensions { get; set; }


/// <summary>
/// Converts this surrogate into the strongly-typed
Expand Down Expand Up @@ -122,6 +147,43 @@ public IConfiguration ToConfiguration()
configuration.Specifications = specifications;
}

// Vendor Extensions — every unrecognized child element captured by
// [XmlAnyElement] projects to an XElement on the model. Elements are
// preserved verbatim so downstream consumers see the exact
// vendor-namespaced XML the operator authored.
if (VendorExtensions != null && VendorExtensions.Length > 0)
{
var extensions = new List<XElement>(VendorExtensions.Length);
foreach (var element in VendorExtensions)
{
if (element != null)
{
// Defense-in-depth against XML External Entity (XXE)
// attacks: .NET 6+ already defaults XmlResolver to
// null and disables DTD processing on the outer
// document read (and a DOCTYPE cannot legally appear
// inside a captured element's OuterXml), but pinning
// both explicitly here mirrors the repo-wide
// convention (see XmiDeserializer.FromXml) and
// survives a future framework downgrade or
// accidental restoration of XmlUrlResolver.
var settings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Prohibit,
XmlResolver = null,
};
using var stringReader = new StringReader(element.OuterXml);
using var xmlReader = XmlReader.Create(stringReader, settings);
extensions.Add(XElement.Load(xmlReader, LoadOptions.PreserveWhitespace));
}
}

if (extensions.Count > 0)
{
configuration.VendorExtensions = extensions;
}
}

return configuration;
}

Expand Down Expand Up @@ -196,6 +258,20 @@ public static void WriteXml(XmlWriter writer, IConfiguration configuration, bool
writer.WriteEndElement();
}

// Write Vendor Extensions — each XElement is a fully-formed,
// vendor-namespaced element that a vendor XSD declares as a
// substitution of the standard AbstractConfiguration abstract
// element. Serialize verbatim via WriteRaw so namespace
// declarations and mixed content are preserved as authored.
if (configuration.VendorExtensions != null)
{
foreach (var extension in configuration.VendorExtensions)
{
if (extension == null) continue;
writer.WriteRaw(extension.ToString(SaveOptions.DisableFormatting));
}
}

writer.WriteEndElement();
}
}
Expand Down
Loading
Loading