A practical guide to XSD tools available in .NET8 environment.
Abstract: A practical guide to XML and XSD tools available in .NET8 environment, focusing on generating and using C# classes to process some XML valid for some given XSD (technology as of September 2024).
1 Doing XML and XSD related work in .NET8
I was recently doing some work related to XML and XSD processing in .NET8 environment and created several proof-of-concept applications to evaluate the tools available. These articles are the result of my prototyping work.
1.1 List of tools used/tested
Here are the tools used/tested:
- Visual Studio 2022
- XSD.EXE (Microsoft license, part of VS2022)
- XmlSchemaClassGenerator (Open Source/Freeware)
- LinqToXsdCore (Open Source/Freeware)
- Liquid XML Objects (Commercial license)
1.2 Articles in this series
For technical reasons, I will organize this text into several articles:
- XSD Tools in .NET8 – Part1 – VS2022
- XSD Tools in .NET8 – Part2 – C# validation
- XSD Tools in .NET8 – Part3 – XsdExe – Simple
- XSD Tools in .NET8 – Part4 – XsdExe - Advanced
- XSD Tools in .NET8 – Part5 – XmlSchemaClassGenerator – Simple
- XSD Tools in .NET8 – Part6 – XmlSchemaClassGenerator – Advanced
- XSD Tools in .NET8 – Part7 – LinqToXsdCore – Simple
- XSD Tools in .NET8 – Part8 – LinqToXsdCore – Advanced
- XSD Tools in .NET8 – Part9 – LiquidXMLObjects – Simple
- XSD Tools in .NET8 – Part10 – LiquidXMLObjects – Advanced
2 More theory about XML and XSD rules
Here is some more theory about XML and XSD rules.
2.1 Optional Xml-Element and Xml-Attribute
Optional: Does not need to be present in the XML.
For XSD Schema elements:
Optional: minOccurs="0" attribute ->In order to set a schema element as optional, you include the minOccurs="0" attribute
2.2 The Difference Between Optional and Not Required for Xml-Element and Xml-Attribute
Note the difference:
- Optional: Does not need to be present in the XML.
- Not Required: Does not need to have a value.
You can have any combination:
- Optional + Not Required
- Optional + Required
- Not Optional + Not Required
- Not Optional + Required
For XSD Schema elements:
- Optional: minOccurs="0" attribute ->In order to set a schema element as optional, you include the minOccurs="0" attribute
- Required: nillable="true" attribute ->In order to set a schema element as not required, you include the nillable="true" attribute.
String data types are not required by default, though you can force them to be required.
Other data types, such as Boolean, Integer, Date, Time, etc. are all required by default. In order to make one of these data types not required, you must set the nillable attribute equal to true for the element in the schema.
3 Examples of XML and XSD
Here are some sample XML-s and XSD-s I created for test purposes.
3.1 Advanced case
Please note that this example XML/XSD has an Optional and Not-Required Xml-Element. Read the comments inside for more details.
<?xml version="1.0" encoding="utf-8"?> <!--SmallCompany.xsd++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="https://markpelf.com/SmallCompany.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="SmallCompany"> <xs:complexType> <xs:sequence> <xs:element name="CompanyName" type="xs:string" /> <xs:element maxOccurs="unbounded" name="Employee"> <xs:complexType> <xs:sequence> <!--Name_String_NO is String NotOptional--> <xs:element name="Name_String_NO" type="xs:string" /> <!--City_String_O is String Optional--> <xs:element minOccurs="0" name="City_String_O" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:element> <xs:element maxOccurs="unbounded" name="InfoData"> <xs:complexType> <xs:sequence> <!--Id_Int_NO is Int NotOptional--> <xs:element name="Id_Int_NO" type="xs:int" /> <!--Quantity_Int_O is Int Optional--> <xs:element minOccurs="0" name="Quantity_Int_O" type="xs:int" /> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
<?xml version="1.0" encoding="utf-8"?> <!--SmallCompanyAAA.xml+++++++++++++++++++++++++++++++++++++++++++++++--> <SmallCompany xmlns="https://markpelf.com/SmallCompany.xsd"> <CompanyName>SmallCompanyAAA</CompanyName> <Employee> <Name_String_NO>Mark</Name_String_NO> <City_String_O>Belgrade</City_String_O> </Employee> <Employee> <Name_String_NO>John</Name_String_NO> </Employee> <InfoData> <Id_Int_NO>11</Id_Int_NO> <Quantity_Int_O>123</Quantity_Int_O> </InfoData> <InfoData> <Id_Int_NO>22</Id_Int_NO> </InfoData> </SmallCompany>
4 Using XmlSchemaClassGenerator tool to create C# class
We focus in this article on the usage of XmlSchemaClassGenerator tool to generate C# class from XSD file.
Here is the tool's basic info.
Tool name============================ XmlSchemaClassGenerator License============================ Open Source/Freeware Where to get it============================ https://github.com/mganss/XmlSchemaClassGenerator?tab=readme-ov-file Version============================ Windows PowerShell > ./XmlSchemaClassGenerator.Console.exe Usage: xscgen [OPTIONS]+ xsdFile... Generate C# classes from XML Schema files. Version 2.1.1162.0 Help============================ Windows PowerShell > ./XmlSchemaClassGenerator.Console.exe Usage: xscgen [OPTIONS]+ xsdFile... Generate C# classes from XML Schema files. Version 2.1.1162.0 xsdFiles may contain globs, e.g. "content\{schema,xsd}\**\*.xsd", and URLs. Append - to option to disable it, e.g. --interface-. Options: -h, --help show this message and exit -n, --namespace=VALUE map an XML namespace to a C# namespace Separate XML namespace and C# namespace by '='. A single value (no '=') is taken as the C# namespace the empty XML namespace is mapped to. One option must be given for each namespace to be mapped. A file name may be given by appending a pipe sign (|) followed by a file name (like schema. xsd) to the XML namespace. If no mapping is found for an XML namespace, a name is generated automatically (may fail). --nf, --namespaceFile=VALUE file containing mappings from XML namespaces to C# namespaces The line format is one mapping per line: XML namespace = C# namespace [optional file name]. Lines starting with # and empty lines are ignored. --tns, --typeNameSubstitute=VALUE substitute a generated type/member name Separate type/member name and substitute name by '='. Prefix type/member name with an appropriate kind ID as documented at: https://t.ly/HHEI. Prefix with 'A:' to substitute any type/member. --tnsf, --typeNameSubstituteFile=VALUE file containing generated type/member name substitute mappings The line format is one mapping per line: prefixed type/member name = substitute name. Lines starting with # and empty lines are ignored. -o, --output=FOLDER the FOLDER to write the resulting .cs files to -d, --datetime-offset map xs:datetime and derived types to System. DateTimeOffset instead of System.DateTime -i, --integer=TYPE map xs:integer and derived types to TYPE instead of automatic approximation TYPE can be i[nt], l[ong], or d[ecimal] --fb, --fallback, --use-integer-type-as-fallback use integer type specified via -i only if no type can be deduced -e, --edb, --enable-data-binding enable INotifyPropertyChanged data binding -r, --order emit order for all class members stored as XML element -c, --pcl PCL compatible output -p, --prefix=PREFIX the PREFIX to prepend to auto-generated namespace names -v, --verbose print generated file names on stdout -0, --nullable generate nullable adapter properties for optional elements/attributes w/o default values -f, --ef generate Entity Framework Code First compatible classes -t, --interface generate interfaces for groups and attribute groups (default is enabled) -a, --pascal use Pascal case for class and property names ( default is enabled) --av, --assemblyVisible use the internal visibility modifier (default is false) -u, --enableUpaCheck should XmlSchemaSet check for Unique Particle Attribution (UPA) (default is enabled) --ct, --collectionType=VALUE collection type to use (default is System. Collections.ObjectModel.Collection`1) --cit, --collectionImplementationType=VALUE the default collection type implementation to use ( default is null) --csm, --collectionSettersMode=Private, Public, PublicWithoutConstructorInitialization, Init, InitWithoutConstructorInitialization generate a private, public, or init-only setter with or without backing field initialization for collections (default is Private; can be: Private, Public, PublicWithoutConstructorInitialization, Init, InitWithoutConstructorInitialization) --ctro, --codeTypeReferenceOptions=GlobalReference, GenericTypeParameter the default CodeTypeReferenceOptions Flags to use ( default is unset; can be: GlobalReference, GenericTypeParameter) --tvpn, --textValuePropertyName=VALUE the name of the property that holds the text value of an element (default is Value) --dst, --debuggerStepThrough generate DebuggerStepThroughAttribute (default is enabled) --dc, --disableComments do not include comments from xsd --nu, --noUnderscore do not generate underscore in private member name ( default is false) --da, --description generate DescriptionAttribute (default is true) --cc, --complexTypesForCollections generate complex types for collections (default is true) -s, --useShouldSerialize use ShouldSerialize pattern instead of Specified pattern (default is false) --sf, --separateFiles generate a separate file for each class (default is false) --nh, --namespaceHierarchy generate a separate folder for namespace hierarchy. Implies "separateFiles" if true (default is false) --sg, --separateSubstitutes generate a separate property for each element of a substitution group (default is false) --dnfin, --doNotForceIsNullable do not force generator to emit IsNullable = true in XmlElement annotation for nillable elements when element is nullable (minOccurs < 1 or parent element is choice) (default is false) --cn, --compactTypeNames use type names without namespace qualifier for types in the using list (default is false) --cl, --commentLanguages=VALUE comment languages to use (default is en; supported are en, de) --un, --uniqueTypeNames generate type names that are unique across namespaces (default is false) --gc, --generatedCodeAttribute add version information to GeneratedCodeAttribute ( default is true) --nc, --netCore generate .NET Core specific code that might not work with .NET Framework (default is false) --nr, --nullableReferenceAttributes generate attributes for nullable reference types ( default is false) --ar, --useArrayItemAttribute use ArrayItemAttribute for sequences with single elements (default is true) --es, --enumAsString Use string instead of enum for enumeration --dmb, --disableMergeRestrictionsWithBase Disable merging of simple type restrictions with base type restrictions --ca, --commandArgs generate a comment with the exact command line arguments that were used to generate the source code (default is true) --uc, --unionCommonType generate a common type for unions if possible ( default is false) --ec, --serializeEmptyCollections serialize empty collections (default is false) --dtd, --allowDtdParse allows dtd parse (default is false) --ns, --namingScheme=VALUE use the specified naming scheme for class and property names (default is Pascal; can be: Direct, Pascal, Legacy) ================================================= Usage Examples=================== Instructions to generate C# class Windows PowerShell> ./XmlSchemaClassGenerator.Console.exe --namespace=Example2SmallCompany --nullable --netCore --nullableReferenceAttributes --namingScheme=Direct SmallCompany.xsd Windows PowerShell> ./XmlSchemaClassGenerator.Console.exe --namespace=Example2BigCompany --nullable --netCore --nullableReferenceAttributes --namingScheme=Direct BigCompany.xsd ============================
5 Generated C# class
Here is the C# generated by the above tool based on the above presented XSD BigCompany.xsd.
Here is the class's full code.
//BigCompany_ver2_2.cs //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ // This code was generated by XmlSchemaClassGenerator version 2.1.1162.0 using the following command: // XmlSchemaClassGenerator.Console --namespace=Example2BigCompany --nullable --netCore --nullableReferenceAttributes --namingScheme=Direct BigCompany.xsd namespace Example2BigCompany { [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1162.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("BigCompany", Namespace="https://markpelf.com/BigCompany.xsd", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlRootAttribute("BigCompany", Namespace="https://markpelf.com/BigCompany.xsd")] public partial class BigCompany { [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("CompanyName")] public string CompanyName { get; set; } [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection<BigCompanyEmployee> _employee; [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Employee")] public System.Collections.ObjectModel.Collection<BigCompanyEmployee> Employee { get { return _employee; } private set { _employee = value; } } /// <summary> /// <para xml:lang="en">Initializes a new instance of the <see cref="BigCompany" /> class.</para> /// </summary> public BigCompany() { this._employee = new System.Collections.ObjectModel.Collection<BigCompanyEmployee>(); this._infoData = new System.Collections.ObjectModel.Collection<BigCompanyInfoData>(); } [System.Xml.Serialization.XmlIgnoreAttribute()] private System.Collections.ObjectModel.Collection<BigCompanyInfoData> _infoData; [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("InfoData")] public System.Collections.ObjectModel.Collection<BigCompanyInfoData> InfoData { get { return _infoData; } private set { _infoData = value; } } } [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1162.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("BigCompanyEmployee", Namespace="https://markpelf.com/BigCompany.xsd", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] public partial class BigCompanyEmployee { [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Name_String_NO")] public string Name_String_NO { get; set; } [System.Diagnostics.CodeAnalysis.AllowNullAttribute()] [System.Diagnostics.CodeAnalysis.MaybeNullAttribute()] [System.Xml.Serialization.XmlElementAttribute("City_String_O")] public string City_String_O { get; set; } } [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1162.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("BigCompanyInfoData", Namespace="https://markpelf.com/BigCompany.xsd", AnonymousType=true)] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] public partial class BigCompanyInfoData { [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Data1_Int_NO_R")] public int Data1_Int_NO_R { get; set; } [System.ComponentModel.DataAnnotations.RequiredAttribute(AllowEmptyStrings=true)] [System.Xml.Serialization.XmlElementAttribute("Data2_Int_NO_NR", IsNullable=true)] public System.Nullable<int> Data2_Int_NO_NR { get; set; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("Data3_Int_O_R")] public int Data3_Int_O_RValue { get; set; } /// <summary> /// <para xml:lang="en">Gets or sets a value indicating whether the Data3_Int_O_R property is specified.</para> /// </summary> [System.Xml.Serialization.XmlIgnoreAttribute()] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public bool Data3_Int_O_RValueSpecified { get; set; } [System.Xml.Serialization.XmlIgnoreAttribute()] public System.Nullable<int> Data3_Int_O_R { get { if (this.Data3_Int_O_RValueSpecified) { return this.Data3_Int_O_RValue; } else { return null; } } set { this.Data3_Int_O_RValue = value.GetValueOrDefault(); this.Data3_Int_O_RValueSpecified = value.HasValue; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] [System.Xml.Serialization.XmlElementAttribute("Data4_Int_O_NR", IsNullable=true)] public System.Nullable<int> Data4_Int_O_NRValue { get; set; } /// <summary> /// <para xml:lang="en">Gets or sets a value indicating whether the Data4_Int_O_NR property is specified.</para> /// </summary> [System.Xml.Serialization.XmlIgnoreAttribute()] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public bool Data4_Int_O_NRValueSpecified { get; set; } [System.Xml.Serialization.XmlIgnoreAttribute()] public System.Nullable<int> Data4_Int_O_NR { get { if (this.Data4_Int_O_NRValueSpecified) { return this.Data4_Int_O_NRValue; } else { return null; } } set { this.Data4_Int_O_NRValue = value.GetValueOrDefault(); this.Data4_Int_O_NRValueSpecified = value.HasValue; } } } }
Here is the class diagram.
6 Two C# API styles for Optional Xml-Elements
There are two approaches/styles for marking Optional Xml-Element presence in generated C# code:
- The first is bool_flag_style – using a bool flag to indicate the presence of optional Xml-Element, with flag=false to indicate the Xml-Element was not present. For example, for some Xml-Element ElemA that, if present, will have value integer, you will get in C# generated two variables “bool ElemA_flag, int ElemA_value”. You need to check if element ElemA was present by first checking the flag ElemA_flag; and then if it is true, you go for the value of ElemA_value. If you do not check flag ElemA_flag first, and just go for the value of ElemA_value you might pick the default int value of zero (0), and you can not know if that is just the default value for C# variable that is always present, but Xml-Element was not present, or that element was present and it actually had the value of zero (0).
- The second is nullable_type_style – using a nullable type to indicate the presence of Xml-Element, with value=null to indicate the Xml-Element was not present. For example, for some Xml-Element ElemA that, if present, will have value integer, you will get in C# generated variable “int? ElemA_nullableValue”. You need to check if element ElemA was present by first checking the ElemA_nullableValue not being null; and then if it is not meaning the element was present, you go for the int value of ElemA_nullableValue.
7 Sample C# app
Here is a sample C# code using the above generated C# class to load and process the above presented XML BigCompanyMMM.xml.
public static void ProcessVer2_Process2( string? filePath, Microsoft.Extensions.Logging.ILogger? logger) { try { logger?.LogInformation( "+++ProcessVer2_Process2-Start++++++++++++++++++"); logger?.LogInformation("filePath:" + filePath); XmlSerializer ser = new XmlSerializer(typeof(Example2BigCompany.BigCompany)); TextReader textReader = File.OpenText(filePath ?? String.Empty); Example2BigCompany.BigCompany? xmlObject = ser.Deserialize(textReader) as Example2BigCompany.BigCompany; if (xmlObject != null) { logger?.LogInformation("CompanyName:" + xmlObject.CompanyName); foreach (Example2BigCompany.BigCompanyEmployee item in xmlObject.Employee) { logger?.LogInformation("------------"); logger?.LogInformation("Name_String_NO:" + item.Name_String_NO); logger?.LogInformation("City_String_O:" + (item.City_String_O ?? "null")); } foreach (Example2BigCompany.BigCompanyInfoData item in xmlObject.InfoData) { logger?.LogInformation("------------"); logger?.LogInformation("Data1_Int_NO_R:" + item.Data1_Int_NO_R.ToString()); logger?.LogInformation("Data2_Int_NO_NR:" + (item.Data2_Int_NO_NR?.ToString() ?? "null")); logger?.LogInformation("Data3_Int_O_RValue:" + item.Data3_Int_O_RValue.ToString()); logger?.LogInformation("Data3_Int_O_RValueSpecified:" + item.Data3_Int_O_RValueSpecified.ToString()); logger?.LogInformation("Data3_Int_O_R:" + (item.Data3_Int_O_R?.ToString() ?? "null")); logger?.LogInformation("Data4_Int_O_NRValue:" + (item.Data4_Int_O_NRValue?.ToString() ?? "null")); logger?.LogInformation("Data4_Int_O_NRValueSpecified:" + item.Data4_Int_O_NRValueSpecified.ToString()); logger?.LogInformation("Data4_Int_O_NR:" + (item.Data4_Int_O_NR?.ToString() ?? "null")); } } else { logger?.LogError("xmlObject == null"); } logger?.LogInformation( "+++ProcessVer2_Process2-End++++++++++++++++++"); } catch (Exception ex) { string methodName = $"Type: {System.Reflection.MethodBase.GetCurrentMethod()?.DeclaringType?.FullName}, " + $"Method: ProcessVer2_Process2; "; logger?.LogError(ex, methodName); } }
And here is the log of execution.
+++ProcessVer2_Process2-Start++++++++++++++++++ filePath:C:\TmpXSD\XsdExample_Ver2\Example01\bin\Debug\net8.0\XmlFiles\BigCompanyMMM.xml CompanyName:BigCompanyMMM ------------ Name_String_NO:Mark City_String_O:Belgrade ------------ Name_String_NO:John City_String_O:null ------------ Data1_Int_NO_R:555 Data2_Int_NO_NR:16 Data3_Int_O_RValue:333 Data3_Int_O_RValueSpecified:True Data3_Int_O_R:333 Data4_Int_O_NRValue:17 Data4_Int_O_NRValueSpecified:True Data4_Int_O_NR:17 ------------ Data1_Int_NO_R:123 Data2_Int_NO_NR:null Data3_Int_O_RValue:15 Data3_Int_O_RValueSpecified:True Data3_Int_O_R:15 Data4_Int_O_NRValue:null Data4_Int_O_NRValueSpecified:True Data4_Int_O_NR:null ------------ Data1_Int_NO_R:777 Data2_Int_NO_NR:null Data3_Int_O_RValue:0 Data3_Int_O_RValueSpecified:False Data3_Int_O_R:null Data4_Int_O_NRValue:null Data4_Int_O_NRValueSpecified:False Data4_Int_O_NR:null +++ProcessVer2_Process2-End++++++++++++++++++
8 Analysis
It is not easy to understand what is happening here, but it looks like this tool is trying to use both “bool_flag_style” and “nullable_type_style” approaches/styles to mark Optional Xml-Element presence in generated C# code. The user can choose which API approach/style he or she likes. If using API style/aproach “bool_flag_style” in addition it uses a nullable method to indicate “nill” value. If using API style/aproach “nullable_type_style” it has no way to indicate “nill” value.
- Data1_Int_NO_R - is int type and always has value
- Data2_Int_NO_NR - is int? type and the meaning is: 1) null – present but “nill” (we have null here even if the element was present but the value was “nill”) 2) int – present and had value
- Xml-Element Data3_Int_O_R is presented by 3 C# variables: Data3_Int_O_RValueSpecified – is a bool type (“bool_flag_style” API) Data3_Int_O_RValue – is int type (“bool_flag_style” API) Data3_Int_O_R – is int? type (“nullable_type_style” API)
- Using “bool_flag_style” API: 1) Data3_Int_O_RValueSpecified – flag to indicate if the element was present or not 2) Data3_Int_O_RValue – if the above flag is true, then this is the element int value
- Using “nullable_type_style” API: 3) Data3_Int_O_R - – is int? type and the meaning is a) null - means it was not present b) int – present and had value
- Xml-Element Data4_Int_O_NR is presented by 3 C# variables: Data4_Int_O_NRValueSpecified – is a bool type (“bool_flag_style” API) Data4_Int_O_NRValue – is int? type (“bool_flag_style” API) Data4_Int_O_NR – is int? type (“nullable_type_style” API)
- Using “bool_flag_style” API: 1) Data4_Int_O_NRValueSpecified – flag to indicate if the element was present or not 2) Data4_Int_O_NRValue – if the above flag is true, then this is the element value. If the value is null, that means that the element was “nill”, or if it is not null, that is the element int value. It can be confusing here that “null” means the value was “nill”, but that is how it is.
- Using “nullable_type_style” API: 3) Data4_Int_O_NR - is int? type and the meaning is: a) null – means it either was not present or present but “nill”. We can not know which of these 2 cases happened. Strictly speaking, that is a deficiency of this approach/style for code generated. In some cases, such a distinction might be needed. b) int – present and had value
It is interesting to look at this tool when using an old API style/approach “bool_flag_style”. It has the ability in the case of Xml-Element Data4_Int_O_NR to indicate all three states: “not-present”, “present-nill”, “present-int”. Just look carefully, and you will see it can. In the case above value (Data4_Int_O_NRValueSpecified, Data4_Int_O_NRValue)=(true, null) means “present-nill”.
I do not see that using the modern API style/approach “nullable_type_style” the same can be achieved. If Data4_Int_O_NR is null, we can not know if it was “not-present” or “present-nill”.
9 Conclusion
This tool XmlSchemaClassGenerator is very interesting and available as a freeware. Code generated worked solid in my test. It can be of great interest to users that want to use nullable_type_style API, which is generally more modern approach to handling the Optional Xml-Elements.
The full example code project can be downloaded at GitHub [99].
10 References
[1] XML Schema
https://www.w3schools.com/xml/xml_schema.asp
[2] The Difference Between Optional and Not Required
https://www.infopathdev.com/blogs/greg/archive/2004/09/16/The-Difference-Between-Optional-and-Not-Required.aspx
[3] nillable and minOccurs XSD element attributes
https://stackoverflow.com/questions/1903062/nillable-and-minoccurs-xsd-element-attributes
Top comments (0)