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
3 Examples of XML and XSD
Here are some sample XML-s and XSD-s I created for test purposes.
3.1 Simple case
Please note that this example XML/XSD has an Optional Xml-Element. Read the comments inside for more details.
="1.0"="utf-8"
<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>
<xs:element name="Name_String_NO" type="xs:string" />
<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>
<xs:element name="Id_Int_NO" type="xs:int" />
<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>
="1.0"="utf-8"
<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 LiquidXMLObjects tool to create C# class
We focus in this article on the usage of LiquidXMLObjects tool to generate C# class from XSD file.
Here is the tool's basic info.
Tool name: ============================
Liquid XML Objects
License============================
2 licenses:
1) Free Community Edition: Home Users and Students, XML Schema (size limited)
2) Commercial Product, Developer Bundle (Installed) $770.94 (perpetual license)
Where to get it============================
https://www.liquid-technologies.com/xml-objects
Install Instructions====================================
-a free trial which will expire 15 days after activation, after this time the product will continue to operate as the Free Community Edition.
Version============================
Liquid Studio - Community Edition 20.7.14.13112
Help============================
https://www.liquid-technologies.com/Reference/XmlDataBinding/xml-objects-introduction.html
====================================================
Usage Examples===================
Instructions to generate C# class
Using GUI - context right click on .xsd file
====================================
4.1 GUI
This commercial product has an impressive GUI to issue commands. Here is how C# classes are generated.
You use the right-click menu to invoke C# class generation
And here are the options I set for C# code generation.
5 Generated C# class
Here is the C# generated by the above tool based on the above presented XSD SmallCompany.xsd.
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Numerics;
using LiquidTechnologies.XmlObjects;
using LiquidTechnologies.XmlObjects.Attribution;
namespace XsdExample_Ver4.XSD1
{
#region Global Settings
[LxRuntimeRequirements("20.7.14.13112", "Free Community Edition", "M9WDQLWDQNEEVNLX", LiquidTechnologies.XmlObjects.LicenseTermsType.CommunityEdition)]
public partial class LxRuntimeRequirementsWritten
{
}
#endregion
}
namespace XsdExample_Ver4.XSD1.Xs
{
#region Complex Types
[LxSimpleComplexTypeDefinition("anyType", "http://www.w3.org/2001/XMLSchema")]
public partial class AnyTypeCt : XElement
{
public AnyTypeCt() : base(XName.Get("anyType", "http://www.w3.org/2001/XMLSchema")) { }
}
#endregion
}
namespace XsdExample_Ver4.XSD1.Tns
{
#region Elements
[LxSimpleElementDefinition("SmallCompany", "https://markpelf.com/SmallCompany.xsd", ElementScopeType.GlobalElement)]
public partial class SmallCompanyElm
{
[LxElementValue(0, "CompanyName", "https://markpelf.com/SmallCompany.xsd", LxValueType.Value, XsdType.XsdString, MinOccurs = 1, MaxOccurs = 1)]
public System.String CompanyName { get; set; } = "";
[LxElementRef(1, MinOccurs = 0, MaxOccurs = LxConstants.Unbounded)]
public List<XsdExample_Ver4.XSD1.Tns.SmallCompanyElm.EmployeeElm> Employees { get; } = new List<XsdExample_Ver4.XSD1.Tns.SmallCompanyElm.EmployeeElm>();
[LxElementRef(2, MinOccurs = 0, MaxOccurs = LxConstants.Unbounded)]
public List<XsdExample_Ver4.XSD1.Tns.SmallCompanyElm.InfoDataElm> InfoData { get; } = new List<XsdExample_Ver4.XSD1.Tns.SmallCompanyElm.InfoDataElm>();
[LxSimpleElementDefinition("Employee", "https://markpelf.com/SmallCompany.xsd", ElementScopeType.InlineElement)]
public partial class EmployeeElm
{
[LxElementValue(0, "Name_String_NO", "https://markpelf.com/SmallCompany.xsd", LxValueType.Value, XsdType.XsdString, MinOccurs = 1, MaxOccurs = 1)]
public System.String Name_String_NO { get; set; } = "";
[LxElementValue(1, "City_String_O", "https://markpelf.com/SmallCompany.xsd", LxValueType.Value, XsdType.XsdString, MinOccurs = 0, MaxOccurs = 1)]
public System.String? City_String_O { get; set; }
}
[LxSimpleElementDefinition("InfoData", "https://markpelf.com/SmallCompany.xsd", ElementScopeType.InlineElement)]
public partial class InfoDataElm
{
[LxElementValue(0, "Id_Int_NO", "https://markpelf.com/SmallCompany.xsd", LxValueType.Value, XsdType.XsdInt, MinOccurs = 1, MaxOccurs = 1)]
public System.Int32 Id_Int_NO { get; set; }
[LxElementValue(1, "Quantity_Int_O", "https://markpelf.com/SmallCompany.xsd", LxValueType.Value, XsdType.XsdInt, MinOccurs = 0, MaxOccurs = 1)]
public System.Int32? Quantity_Int_O { get; set; }
}
}
#endregion
}
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 SmallCompanyAAA.xml.
public static void ProcessVer4_Process1(
string? filePath,
Microsoft.Extensions.Logging.ILogger? logger)
{
try
{
logger?.LogInformation(
"+++ProcessVer4_Process1-Start++++++++++++++++++");
logger?.LogInformation("filePath:" + filePath);
LxSerializer<XsdExample_Ver4.XSD1.Tns.SmallCompanyElm> serializer =
new LxSerializer<XsdExample_Ver4.XSD1.Tns.SmallCompanyElm>();
TextReader textReader = File.OpenText(filePath ?? String.Empty);
LxReaderSettings readerSettings = new LxReaderSettings();
XsdExample_Ver4.XSD1.Tns.SmallCompanyElm? xmlObject =
serializer.Deserialize(textReader, readerSettings);
if (xmlObject != null)
{
logger?.LogInformation("CompanyName:" + xmlObject.CompanyName);
foreach(XsdExample_Ver4.XSD1.Tns.SmallCompanyElm.EmployeeElm item in xmlObject.Employees)
{
logger?.LogInformation("------------" );
logger?.LogInformation("Name_String_NO:" + item.Name_String_NO);
logger?.LogInformation("City_String_O:" + (item.City_String_O ?? "null") );
}
foreach (XsdExample_Ver4.XSD1.Tns.SmallCompanyElm.InfoDataElm item in xmlObject.InfoData)
{
logger?.LogInformation("------------");
logger?.LogInformation("Id_Int_NO:" + item.Id_Int_NO.ToString());
logger?.LogInformation("Quantity_Int_O:" + (item.Quantity_Int_O?.ToString() ?? "null"));
}
}
else
{
logger?.LogError("xmlObject == null");
}
logger?.LogInformation(
"+++ProcessVer4_Process1-End++++++++++++++++++");
}
catch (Exception ex)
{
string methodName =
$"Type: {System.Reflection.MethodBase.GetCurrentMethod()?.DeclaringType?.FullName}, " +
$"Method: ProcessVer4_Process1; ";
logger?.LogError(ex, methodName);
}
}
And here is the log of execution.
+++ProcessVer4_Process1-Start++++++++++++++++++
filePath:C:\TmpXSD\XsdExample_Ver4\Example01\bin\Debug\net8.0\XmlFiles\SmallCompanyAAA.xml
CompanyName:SmallCompanyAAA
------------
Name_String_NO:Mark
City_String_O:Belgrade
------------
Name_String_NO:John
City_String_O:null
------------
Id_Int_NO:11
Quantity_Int_O:123
------------
Id_Int_NO:22
Quantity_Int_O:null
+++ProcessVer4_Process1-End++++++++++++++++++
8 Analysis
This tool uses “nullable_type_style” approach/style to mark Optional Xml-Element presence in generated C# code.
- Quantity_Int_O– is int? type and the meaning is
a) null - means it was not present
b) int – present and had value
9 Conclusion
This tool LiquidXMLObjects is very interesting but requires a commercial license. The code generated looked solid in my test. It can be of great interest to users who want to use nullable_type_style API, which is generally a more modern approach for handling the Optional Xml-Elements.
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
[21] XSD Tools in .NET8 – Part1 – VS2022
https://www.codeproject.com/Articles/5388391/XSD-Tools-in-NET8-Part1-VS2022
[22] XSD Tools in .NET8 – Part2 – C# validation
https://www.codeproject.com/Articles/5388393/XSD-Tools-in-NET8-Part2-Csharp-validation
[23] XSD Tools in .NET8 – Part3 – XsdExe – Simple
https://www.codeproject.com/Articles/5388396/XSD-Tools-in-NET8-Part3-XsdExe-Simple
[24] XSD Tools in .NET8 – Part4 – XsdExe - Advanced
https://www.codeproject.com/Articles/5388483/XSD-Tools-in-NET8-Part4-XsdExe-Advanced
[25] XSD Tools in .NET8 – Part5 – XmlSchemaClassGenerator – Simple
https://www.codeproject.com/Articles/5388548/XSD-Tools-in-NET8-Part5-XmlSchemaClassGenerator-Si
[26] XSD Tools in .NET8 – Part6 – XmlSchemaClassGenerator – Advanced
https://www.codeproject.com/Articles/5388549/XSD-Tools-in-NET8-Part6-XmlSchemaClassGenerator-Ad
[27] XSD Tools in .NET8 – Part7 – LinqToXsdCore – Simple
https://www.codeproject.com/Articles/5388628/XSD-Tools-in-NET8-Part7-LinqToXsdCore-Simple
[28] XSD Tools in .NET8 – Part8 – LinqToXsdCore – Advanced
https://www.codeproject.com/Articles/5388629/XSD-Tools-in-NET8-Part8-LinqToXsdCore-Advanced
[29] XSD Tools in .NET8 – Part9 – LiquidXMLObjects – Simple
https://www.codeproject.com/Articles/5388683/XSD-Tools-in-NET8-Part9-LiquidXMLObjects-Simple
[30] XSD Tools in .NET8 – Part10 – LiquidXMLObjects – Advanced
https://www.codeproject.com/Articles/5388684/XSD-Tools-in-NET8-Part10-LiquidXMLObjects-Advanced