65.9K
CodeProject 正在变化。 阅读更多。
Home

XSD 工具在 .NET8 中 – 第 10 部分 – LiquidXMLObjects - 高级

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2024 年 9 月 28 日

CPOL

6分钟阅读

viewsIcon

2042

downloadIcon

90

.NET8 环境中可用的 XSD 工具实用指南。

1 在 .NET8 中进行 XML 和 XSD 相关工作

最近,我在 .NET8 环境中进行了一些与 XML 和 XSD 处理相关的工作,并创建了几个 概念验证应用程序来评估可用的工具。这些文章是我 原型工作的结果。

 

1.1 使用/测试的工具列表

以下是使用/测试的工具

  • Visual Studio 2022
  • XSD.EXE(微软许可,VS2022 的一部分)
  • XmlSchemaClassGenerator (开源/免费软件)
  • LinqToXsdCore (开源/免费软件)
  • Liquid XML Objects (商业许可)

 

1.2 本系列文章

由于技术原因,我将把这篇文章组织成几篇文章

  • .NET8 中的 XSD 工具 – 第 1 部分 – VS2022
  • .NET8 中的 XSD 工具 – 第 2 部分 – C# 验证
  • .NET8 中的 XSD 工具 – 第 3 部分 – XsdExe – 简单
  • .NET8 中的 XSD 工具 – 第 4 部分 – XsdExe - 高级
  • .NET8 中的 XSD 工具 – 第 5 部分 – XmlSchemaClassGenerator – 简单
  • .NET8 中的 XSD 工具 – 第 6 部分 – XmlSchemaClassGenerator – 高级
  • .NET8 中的 XSD 工具 – 第 7 部分 – LinqToXsdCore – 简单
  • .NET8 中的 XSD 工具 – 第 8 部分 – LinqToXsdCore – 高级
  • .NET8 中的 XSD 工具 – 第 9 部分 – LiquidXMLObjects – 简单
  • .NET8 中的 XSD 工具 – 第 10 部分 – LiquidXMLObjects – 高级

 

2 更多关于 XML 和 XSD 规则的理论

这里有更多关于 XML 和 XSD 规则的理论。

2.1 可选的 Xml-Element 和 Xml-Attribute

可选:XML 中不需要存在。

对于 XSD Schema 元素
可选:minOccurs="0" 属性 ->要将 Schema 元素设置为可选,请包含 minOccurs="0" 属性。

 

2.2 可选和非必需的 Xml-Element 和 Xml-Attribute 之间的区别

注意区别

  • 可选:XML 中不需要存在。
  • 非必需:不需要有值。

 

您可以进行任何组合

  • 可选 + 非必需
  • 可选 + 必需
  • 非可选 + 非必需
  • 非可选 + 必需

 

对于 XSD Schema 元素

  • 可选:minOccurs="0" 属性 ->要将 Schema 元素设置为可选,请包含 minOccurs="0" 属性。
  • 必需:nillable="true" 属性 ->要将 Schema 元素设置为非必需,请包含 nillable="true" 属性。

 

字符串数据类型默认是非必需的,但您可以强制它们成为必需的。
其他数据类型,例如布尔型、整数型、日期型、时间型等,默认都是必需的。要使这些数据类型中的一个非必需,您必须在 Schema 中为该元素设置 nillable 属性等于 true。

 

3 XML 和 XSD 的示例

以下是我为测试目的创建的一些示例 XML 和 XSD。

3.1 高级案例

请注意,此示例 XML/XSD 包含一个 可选且非必需的 Xml-Element。请阅读其中的注释以获取更多详细信息。

 

<?xml version="1.0" encoding="utf-8"?>
<!--BigCompany.xsd+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-->
<xs:schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           attributeFormDefault="unqualified" 
           elementFormDefault="qualified" 
           targetNamespace="https://markpelf.com/BigCompany.xsd" 
           xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="BigCompany">
        <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>
                            <!--Data1_Int_NO_R is Int NotOptional+Required-->
                            <xs:element name="Data1_Int_NO_R" type="xs:int" minOccurs="1" nillable="false" />
                            <!--Data2_Int_NO_NR is Int NotOptional+NotRequired-->
                            <xs:element name="Data2_Int_NO_NR" type="xs:int" minOccurs="1" nillable="true" />
                            <!--Data3_Int_O_R is Int Optional+Required-->
                            <xs:element name="Data3_Int_O_R" type="xs:int" minOccurs="0" nillable="false" />
                            <!--Data4_Int_O_NR is Int Optional+NotRequired-->
                            <xs:element name="Data4_Int_O_NR" type="xs:int" minOccurs="0" nillable="true" />
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

 

<?xml version="1.0" encoding="utf-8"?>
<!--BigCompanyMMM.xml+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-->
<BigCompany xmlns="https://markpelf.com/BigCompany.xsd">
    <CompanyName>BigCompanyMMM</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>
        <Data1_Int_NO_R>555</Data1_Int_NO_R>
        <Data2_Int_NO_NR>16</Data2_Int_NO_NR>
        <Data3_Int_O_R>333</Data3_Int_O_R>
        <Data4_Int_O_NR>17</Data4_Int_O_NR>
    </InfoData>
    <InfoData>
        <Data1_Int_NO_R>123</Data1_Int_NO_R>
        <Data2_Int_NO_NR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true" />
        <Data3_Int_O_R>15</Data3_Int_O_R>
        <Data4_Int_O_NR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true" />
    </InfoData>
    <InfoData>
        <Data1_Int_NO_R>777</Data1_Int_NO_R>
        <Data2_Int_NO_NR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true" />
    </InfoData>
</BigCompany>

 

4 使用 LiquidXMLObjects 工具创建 C# 类

在本文中,我们将重点介绍使用 LiquidXMLObjects 工具从 XSD 文件生成 C# 类。
以下是该工具的基本信息。

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

这个商业产品有一个令人印象深刻的 GUI 来发出命令。以下是 C# 类生成的方式。

您可以使用右键菜单来调用 C# 类生成

 

这是我为 C# 代码生成设置的选项。

 

5 生成的 C# 类

这是上面工具根据上面提供的 XSD BigCompany.xsd 生成的 C# 代码。

///////////////////////////////////////////////////////////////////////////
//           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.XSD2
// 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\XSD2\BigCompany.xsd


namespace XsdExample_Ver4.XSD2
{
    #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.XSD2.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.XSD2.Tns
{
    #region Elements
    /// <summary>A class representing the root XSD element BigCompany@https://markpelf.com/BigCompany.xsd</summary>
    [LxSimpleElementDefinition("BigCompany", "https://markpelf.com/BigCompany.xsd", ElementScopeType.GlobalElement)]
    public partial class BigCompanyElm
    {
        /// <summary>A <see cref="System.String" />, Required : should not be set to null</summary>
        [LxElementValue(0, "CompanyName", "https://markpelf.com/BigCompany.xsd", LxValueType.Value, XsdType.XsdString, MinOccurs = 1, MaxOccurs = 1)]
        public System.String CompanyName { get; set; } = "";

        /// <summary>A collection of <see cref="XsdExample_Ver4.XSD2.Tns.BigCompanyElm.EmployeeElm" /></summary>
        [LxElementRef(1, MinOccurs = 0, MaxOccurs = LxConstants.Unbounded)]
        public List<XsdExample_Ver4.XSD2.Tns.BigCompanyElm.EmployeeElm> Employees { get; } = new List<XsdExample_Ver4.XSD2.Tns.BigCompanyElm.EmployeeElm>();

        /// <summary>A collection of <see cref="XsdExample_Ver4.XSD2.Tns.BigCompanyElm.InfoDataElm" /></summary>
        [LxElementRef(2, MinOccurs = 0, MaxOccurs = LxConstants.Unbounded)]
        public List<XsdExample_Ver4.XSD2.Tns.BigCompanyElm.InfoDataElm> InfoData { get; } = new List<XsdExample_Ver4.XSD2.Tns.BigCompanyElm.InfoDataElm>();

        /// <summary>Represent the inline xs:element Employee@https://markpelf.com/BigCompany.xsd.</summary>
        [LxSimpleElementDefinition("Employee", "https://markpelf.com/BigCompany.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/BigCompany.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/BigCompany.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/BigCompany.xsd.</summary>
        [LxSimpleElementDefinition("InfoData", "https://markpelf.com/BigCompany.xsd", ElementScopeType.InlineElement)]
        public partial class InfoDataElm
        {
            /// <summary>A <see cref="System.Int32" />, Required</summary>
            [LxElementValue(0, "Data1_Int_NO_R", "https://markpelf.com/BigCompany.xsd", LxValueType.Value, XsdType.XsdInt, MinOccurs = 1, MaxOccurs = 1)]
            public System.Int32 Data1_Int_NO_R { get; set; }

            /// <summary>A <see cref="System.Int32" />, Required</summary>
            [LxElementValue(1, "Data2_Int_NO_NR", "https://markpelf.com/BigCompany.xsd", LxValueType.Value, XsdType.XsdInt, MinOccurs = 1, MaxOccurs = 1)]
            public LxNillable<System.Int32> Data2_Int_NO_NR { get; set; } = LxNillable<System.Int32>.Create(default(System.Int32), false);

            /// <summary>A nullable <see cref="System.Int32" />, Optional : null when not set</summary>
            [LxElementValue(2, "Data3_Int_O_R", "https://markpelf.com/BigCompany.xsd", LxValueType.Value, XsdType.XsdInt, MinOccurs = 0, MaxOccurs = 1)]
            public System.Int32? Data3_Int_O_R { get; set; }

            /// <summary>A nullable <see cref="System.Int32" />, Optional : null when not set</summary>
            [LxElementValue(3, "Data4_Int_O_NR", "https://markpelf.com/BigCompany.xsd", LxValueType.Value, XsdType.XsdInt, MinOccurs = 0, MaxOccurs = 1)]
            public LxNillable<System.Int32> Data4_Int_O_NR { get; set; }

        }

    }

    #endregion

}

 

这是类的类图。

 

6 可选 Xml-Elements 的两种 C# API 样式

在生成的 C# 代码中,有两种方法/样式可以标记可选 Xml-Element 的存在。

  1. 第一种是 bool_flag_style – 使用布尔标志来指示可选 Xml-Element 的存在,flag=false 表示 Xml-Element 不存在。
    例如,对于一个 Xml 元素 ElemA,如果存在,其值将是整数,您将在生成的 C# 代码中得到两个变量“bool ElemA_flag, int ElemA_value”。您需要通过首先检查标志 ElemA_flag 来检查元素 ElemA 是否存在;然后,如果为 true,您就可以获取 ElemA_value 的值。如果您不先检查标志 ElemA_flag,而只是获取 ElemA_value 的值,您可能会得到默认整数值零 (0),而您无法知道这只是 C# 变量的默认值(变量始终存在),还是 Xml 元素不存在,或者是元素存在但实际值为零 (0)。
  2. 第二种是 nullable_type_style – 使用可空类型来指示 Xml-Element 的存在,value=null 表示 Xml-Element 不存在。
    例如,对于一个 Xml 元素 ElemA,如果存在,其值将是整数,您将在生成的 C# 代码中得到一个变量“int? ElemA_nullableValue”。您需要通过首先检查 ElemA_nullableValue 是否不为 null 来检查元素 ElemA 是否存在;然后,如果不为 null(表示元素存在),您就可以获取 ElemA_nullableValue 的整数值。

 

7 示例 C# 应用程序

这是一个使用上面生成的 C# 类来加载和处理上面提供的 XML BigCompanyMMM.xml 的示例 C# 代码。

public static void ProcessVer4_Process2(
    string? filePath,
    Microsoft.Extensions.Logging.ILogger? logger)
{
    try
    {
        logger?.LogInformation(
            "+++ProcessVer4_Process2-Start++++++++++++++++++");
        logger?.LogInformation("filePath:" + filePath);

        LxSerializer<XsdExample_Ver4.XSD2.Tns.BigCompanyElm> serializer =
            new LxSerializer<XsdExample_Ver4.XSD2.Tns.BigCompanyElm>();
        TextReader textReader = File.OpenText(filePath ?? String.Empty);
        LxReaderSettings readerSettings = new LxReaderSettings();

        XsdExample_Ver4.XSD2.Tns.BigCompanyElm? xmlObject =
            serializer.Deserialize(textReader, readerSettings);

        if (xmlObject != null)
        {
            logger?.LogInformation("CompanyName:" + xmlObject.CompanyName);

            foreach (XsdExample_Ver4.XSD2.Tns.BigCompanyElm.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.XSD2.Tns.BigCompanyElm.InfoDataElm 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.IsNil ? "nill" : item.Data2_Int_NO_NR.Value));

                logger?.LogInformation("Data3_Int_O_R:" + (item.Data3_Int_O_R?.ToString() ?? "null"));

                logger?.LogInformation("Data4_Int_O_NR:" + 
                    (item.Data4_Int_O_NR==null ? "null" :
                     (item.Data4_Int_O_NR.IsNil ? "nil" : item.Data4_Int_O_NR.Value)));
            }
        }
        else
        {
            logger?.LogError("xmlObject == null");
        }

        logger?.LogInformation(
            "+++ProcessVer4_Process2-End++++++++++++++++++");
    }
    catch (Exception ex)
    {
        string methodName =
            $"Type: {System.Reflection.MethodBase.GetCurrentMethod()?.DeclaringType?.FullName}, " +
            $"Method: ProcessVer4_Process2; ";
        logger?.LogError(ex, methodName);
    }
}

 

这是执行日志。

 +++ProcessVer4_Process2-Start++++++++++++++++++
 filePath:C:\TmpXSD\XsdExample_Ver4\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_R:333
 Data4_Int_O_NR:17
 ------------
 Data1_Int_NO_R:123
 Data2_Int_NO_NR:nill
 Data3_Int_O_R:15
 Data4_Int_O_NR:nil
 ------------
 Data1_Int_NO_R:777
 Data2_Int_NO_NR:nill
 Data3_Int_O_R:null
 Data4_Int_O_NR:null
 +++ProcessVer4_Process2-End++++++++++++++++++


 

 

8 分析

该工具使用“nullable_type_style”方法/样式来标记生成 C# 代码中的可选 Xml 元素。为了指示“nill”,它使用一个特殊的类对象作为结果。

 

  • Data1_Int_NO_R - 是 int 类型,并且始终有值。
  • Data2_Int_NO_NR - 是某个类的对象,可以有两个值:1)“nill”,2)int。
    a)“nill”– 存在但为“nill”
    b) int – 存在并有值。
  • Data3_Int_O_R – 是 int? 类型,其含义是
    1) null - 表示不存在
    2) int – 存在并有值。
  • Data4_Int_O_NR - 是某个类的对象,可以有 3 个值:1)null,2)“nill”,3)int。
    1) null – 表示元素不存在
    2)“nill”– 元素存在但为“nill”
    3) int – 元素存在且有值

 

有趣的是,该工具使用了现代 API 风格/方法“nullable_type_style但是,在 Xml 元素 Data4_Int_O_NR 的情况下,它仍然能够指示所有三种状态:“不存在”、“存在-nill”、“存在-int”。他们通过使用一个特殊的类/对象来表示三元值来实现这一点。

 

9 结论

该工具 LiquidXMLObjects 非常有趣,但需要商业许可。在我进行的测试中,生成的代码看起来很扎实。对于希望使用nullable_type_style API 的用户来说,这可能非常有用,因为它通常是处理可选 Xml 元素的一种更现代的方法。

 

10 参考文献

 

[1] XML 架构
https://w3schools.org.cn/xml/xml_schema.asp

[21] .NET8 中的 XSD 工具 – 第 1 部分 – VS2022
https://codeproject.org.cn/Articles/5388391/XSD-Tools-in-NET8-Part1-VS2022

[22] XSD 工具在 .NET8 中 – 第 2 部分 – C# 验证
https://codeproject.org.cn/Articles/5388393/XSD-Tools-in-NET8-Part2-Csharp-validation

[23] XSD 工具在 .NET8 中 – 第 3 部分 – XsdExe – 简单
https://codeproject.org.cn/Articles/5388396/XSD-Tools-in-NET8-Part3-XsdExe-Simple

[24] XSD 工具在 .NET8 中 – 第 4 部分 – XsdExe - 高级
https://codeproject.org.cn/Articles/5388483/XSD-Tools-in-NET8-Part4-XsdExe-Advanced

[25] XSD 工具在 .NET8 中 – 第 5 部分 – XmlSchemaClassGenerator – 简单
https://codeproject.org.cn/Articles/5388548/XSD-Tools-in-NET8-Part5-XmlSchemaClassGenerator-Si

[26] XSD 工具在 .NET8 中 – 第 6 部分 – XmlSchemaClassGenerator – 高级
https://codeproject.org.cn/Articles/5388549/XSD-Tools-in-NET8-Part6-XmlSchemaClassGenerator-Ad

[27] XSD 工具在 .NET8 中 – 第 7 部分 – LinqToXsdCore – 简单
https://codeproject.org.cn/Articles/5388628/XSD-Tools-in-NET8-Part7-LinqToXsdCore-Simple

[28] XSD 工具在 .NET8 中 – 第 8 部分 – LinqToXsdCore – 高级
https://codeproject.org.cn/Articles/5388629/XSD-Tools-in-NET8-Part8-LinqToXsdCore-Advanced

[29] XSD 工具在 .NET8 中 – 第 9 部分 – LiquidXMLObjects – 简单
https://codeproject.org.cn/Articles/5388683/XSD-Tools-in-NET8-Part9-LiquidXMLObjects-Simple

[30] XSD 工具在 .NET8 中 – 第 10 部分 – LiquidXMLObjects – 高级
https://codeproject.org.cn/Articles/5388684/XSD-Tools-in-NET8-Part10-LiquidXMLObjects-Advanced

 

© . All rights reserved.