Generating correct object for simple types in xml schemas

69 Views Asked by At

I have the ISO 20022 standard .xsd files for camt.026 ISO message and the possible external codes it can use based on it's conf element on the complex type InvestigationStatus5Choice.

camt.026 xsd:

<xs:complexType name="InvestigationStatus5Choice">
    <xs:choice>
        <xs:element name="Conf" type="ExternalInvestigationExecutionConfirmation1Code"/>
        ...
    </xs:choice>
</xs:complexType>

The same camt.026 xsd file also declares this ExternalInvestigationExecutionConfirmation1Code type as

<xs:simpleType name="ExternalInvestigationExecutionConfirmation1Code">
    <xs:restriction base="xs:string">
        <xs:minLength value="1"/>
        <xs:maxLength value="4"/>
    </xs:restriction>
</xs:simpleType>

The external code sets xsd is supposed to have the type declaration for this ExternalInvestigationExecutionConfirmation1Code type with possible enumeration values.

<xs:simpleType name="ExternalInvestigationExecutionConfirmation1Code">
    ...
    <xs:restriction base="xs:string">
        <xs:minLength value="1"/>
        <xs:maxLength value="4"/>
        <xs:enumeration value="ACDA">
            ...
        </xs:enumeration>
        <xs:enumeration value="ACNR">
            ...
        </xs:enumeration>
        <xs:enumeration value="ACVA">
            ...
        ...
        ...

It seems every time I generate code using xsd.exe it generates based off the simple type found in the camt.026 xsd and it is not including the possible enumeration values found in the external code sets xsd.

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:iso:std:iso:20022:tech:xsd:camt.029.001.09")]
public partial class InvestigationStatus5Choice {
    
    private object[] itemsField;
    
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("AssgnmtCxlConf", typeof(bool))]
    [System.Xml.Serialization.XmlElementAttribute("Conf", typeof(string))] // <-- This should be of type ExternalInvestigationExecutionConfirmation1Code 
    [System.Xml.Serialization.XmlElementAttribute("DplctOf", typeof(Case5))]
    [System.Xml.Serialization.XmlElementAttribute("RjctdMod", typeof(ModificationStatusReason1Choice))]
    public object[] Items {
        get {
            return this.itemsField;
        }
        set {
            this.itemsField = value;
        }
    }
}

How can I make it include the enumeration values? I tried putting the external code sets xsd before and after the camt.026 xsd as I figured order mattered but no luck.

My folder structure is unique here but the files are resolvable and I am trying:

xsd.exe /classes ..\..\..\2Q2023_ExternalCodeSets_v1.xsd .\iso\camt.029.001.09.xsd

and

xsd.exe /classes iso\camt.029.001.09.xsd .\..\..\..\2Q2023_ExternalCodeSets_v1.xsd

2

There are 2 best solutions below

0
On BEST ANSWER

The file camt.029.001.12.xsd does not actually reference the external code set file, so XSD.exe can't pick up the enum values defined in the external code set file.

As the other answer mentioned, the decoupling between the 2 schemas is deliberate.

The only way to get XSD.exe to generate enums for the ExternalInvestigationExecutionConfirmation1Code simple type is to modify camt.029.001.12.xsd.

I think you should probably weigh up whether or not it's worth manually modifying the schema for your purposes. I find when using the generated code to create and save new XML documents, having the generated enum values available via Intellisense is really helpful, but if you're using generated code to just read XML documents, it's probably enough to just read the enum values as a plain string.

Open the camt.029.001.12.xsd file, and add the 'ExternalCodeSets' namespace to the xs:schema element:

<xs:schema ...
    xmlns:ecs="urn:iso:std:iso:20022:tech:xsd:externalcodeset">

Then add the xs:import statement (as the first child element of the xs:schema element):

<xs:import namespace="urn:iso:std:iso:20022:tech:xsd:externalcodeset" 
    schemaLocation="2Q2023_ExternalCodeSets_v1.xsd"/>

Then you have to replace the local reference to ExternalInvestigationExecutionConfirmation1Code to the external reference:

<xs:element name="Conf" type="ecs:ExternalInvestigationExecutionConfirmation1Code"/>

So now, the InvestigationStatus5Choice element type looks like:

<xs:complexType name="InvestigationStatus5Choice">
    <xs:choice>
        <xs:element name="Conf" type="ecs:ExternalInvestigationExecutionConfirmation1Code"/>
        <xs:element maxOccurs="unbounded" minOccurs="1" name="RjctdMod"
            type="ModificationStatusReason1Choice"/>
        <xs:element name="DplctOf" type="Case5"/>
        <xs:element name="AssgnmtCxlConf" type="YesNoIndicator"/>
    </xs:choice>
</xs:complexType>

Re run Xsd.exe (xsd 2Q2023_ExternalCodeSets_v1.xsd camt.029.001.12.xsd /classes) and it will then generate:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:iso:std:iso:20022:tech:xsd:camt.029.001.12")]
public partial class InvestigationStatus5Choice {
    
    private object[] itemsField;    

    [System.Xml.Serialization.XmlElementAttribute("AssgnmtCxlConf", typeof(bool))]
    [System.Xml.Serialization.XmlElementAttribute("Conf", typeof(ExternalInvestigationExecutionConfirmation1Code))]
    [System.Xml.Serialization.XmlElementAttribute("DplctOf", typeof(Case5))]
    [System.Xml.Serialization.XmlElementAttribute("RjctdMod", typeof(ModificationStatusReason1Choice))]
    public object[] Items {
        get {
            return this.itemsField;
        }
        set {
            this.itemsField = value;
        }
    }
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")]
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:iso:std:iso:20022:tech:xsd:externalcodeset")]
public enum ExternalInvestigationExecutionConfirmation1Code {
    ACDA, ACNR, ACVA, CHRG, CNCL, CONF, CVAA, CWFW, FTNA, ICOV, IDUP, IPAY, IPYI, MCOV, 
    MODI, MWFW, PDCR, PECR, PURP, RJCR, RJNR, RJVA, SMTC, SMTI, UWFW, BIAS, IDNE, IVCR, 
    INFO, NINF, PDNG, 
}
2
On

I'm pretty sure this is normal generation.

The choice here is to split between the field declaration (here xs:string with minLength = 1 and maxLength = 4) and the actual possible values at some time (declared in the external code)

The purpose and value of externalizing a code set is to allow for a more frequent update of the code set by for example adding new codes in the set without impacting the version of the messages and the development cycle of the messages. In practice the external code sets follow a quarterly update cycle.

If the service adds a new value of enumeration, your base code will not be able to retrieve the value that you'll get in XML in the receiver part and may probably lead to an error (or retrieve an empty / null value in the best cases).

By splitting code like this, you can still generate your enumeration from the external reference and do some comparison based on the received string value but with future-proof evolution of values you'll receive (unknown values won't be processed by your side without changes on your side and it won't fail deserialize data).