DEV Community

Cover image for XSD Tools in .NET8 – Part9 – LiquidXMLObjects- Simple
Mark Pelf
Mark Pelf

Posted on

XSD Tools in .NET8 – Part9 – LiquidXMLObjects- Simple

XSD Tools in .NET8 – Part9 – LiquidXMLObjects- Simple

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

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.

<?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> 
Enter fullscreen mode Exit fullscreen mode
<?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> 
Enter fullscreen mode Exit fullscreen mode

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 ==================================== 
Enter fullscreen mode Exit fullscreen mode

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.

/////////////////////////////////////////////////////////////////////////// // Liquid XML Objects GENERATED CODE - DO NOT MODIFY // // https://www.liquid-technologies.com/xml-objects // //=======================================================================// // Dependencies // // Nuget : LiquidTechnologies.XmlObjects.Runtime // // : MUST BE VERSION 20.7.14 // //=======================================================================// // Online Help // // https://www.liquid-technologies.com/xml-objects-quick-start-guide // //=======================================================================// // Licensing Information // // https://www.liquid-technologies.com/eula // /////////////////////////////////////////////////////////////////////////// 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; // ------------------------------------------------------ // | Settings | // ------------------------------------------------------ // GenerateCommonBaseClass = False // GenerateUnprocessedNodeHandlers = False // RaiseChangeEvents = False // CollectionNaming = Pluralize // Language = CS // OutputNamespace = XsdExample_Ver4.XSD1 // WriteDefaultValuesForOptionalAttributes = True // WriteDefaultValuesForOptionalElements = True // MixedContentHandling = TreatAsAny // GenerationModel = Simple // *WARNING* this simplified model that is very easy to work with // but may cause the XML to be produced without regard for element // cardinality or order. Where very high compliance with the XML Schema // standard is required use GenerationModelType.Conformant // XSD Schema Files // C:\TmpXSD\XsdExample_Ver4\Example01\XSD1\SmallCompany.xsd namespace XsdExample_Ver4.XSD1 { #region Global Settings /// <summary>Contains library level properties, and ensures the version of the runtime used matches the version used to generate it.</summary> [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 /// <summary>A class representing the root XSD complexType anyType@http://www.w3.org/2001/XMLSchema</summary> [LxSimpleComplexTypeDefinition("anyType", "http://www.w3.org/2001/XMLSchema")] public partial class AnyTypeCt : XElement { /// <summary>Constructor : create a <see cref="AnyTypeCt" /> element &lt;anyType xmlns='http://www.w3.org/2001/XMLSchema'&gt;</summary> public AnyTypeCt() : base(XName.Get("anyType", "http://www.w3.org/2001/XMLSchema")) { } } #endregion } namespace XsdExample_Ver4.XSD1.Tns { #region Elements /// <summary>A class representing the root XSD element SmallCompany@https://markpelf.com/SmallCompany.xsd</summary> [LxSimpleElementDefinition("SmallCompany", "https://markpelf.com/SmallCompany.xsd", ElementScopeType.GlobalElement)] public partial class SmallCompanyElm { /// <summary>A <see cref="System.String" />, Required : should not be set to null</summary> [LxElementValue(0, "CompanyName", "https://markpelf.com/SmallCompany.xsd", LxValueType.Value, XsdType.XsdString, MinOccurs = 1, MaxOccurs = 1)] public System.String CompanyName { get; set; } = ""; /// <summary>A collection of <see cref="XsdExample_Ver4.XSD1.Tns.SmallCompanyElm.EmployeeElm" /></summary> [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>(); /// <summary>A collection of <see cref="XsdExample_Ver4.XSD1.Tns.SmallCompanyElm.InfoDataElm" /></summary> [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>(); /// <summary>Represent the inline xs:element Employee@https://markpelf.com/SmallCompany.xsd.</summary> [LxSimpleElementDefinition("Employee", "https://markpelf.com/SmallCompany.xsd", ElementScopeType.InlineElement)] public partial class EmployeeElm { /// <summary>A <see cref="System.String" />, Required : should not be set to null</summary> [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; } = ""; /// <summary>A <see cref="System.String" />, Optional : null when not set</summary> [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; } } /// <summary>Represent the inline xs:element InfoData@https://markpelf.com/SmallCompany.xsd.</summary> [LxSimpleElementDefinition("InfoData", "https://markpelf.com/SmallCompany.xsd", ElementScopeType.InlineElement)] public partial class InfoDataElm { /// <summary>A <see cref="System.Int32" />, Required</summary> [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; } /// <summary>A nullable <see cref="System.Int32" />, Optional : null when not set</summary> [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 } 
Enter fullscreen mode Exit fullscreen mode

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:

  1. 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).
  2. 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); } } 
Enter fullscreen mode Exit fullscreen mode

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++++++++++++++++++ 
Enter fullscreen mode Exit fullscreen mode

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.

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

[99] https://github.com/MarkPelf/XsdToolsInNet8

Top comments (0)