Git Product home page Git Product logo

mamift / linqtoxsdcore Goto Github PK

View Code? Open in Web Editor NEW
39.0 6.0 14.0 25.85 MB

LinqToXsd ported to .NET Core (targets .NET Standard 2 for generated code and .NET Core 3.1, .NET 5+ for the code generator CLI tool).

License: Microsoft Public License

C# 99.78% PowerShell 0.01% Batchfile 0.01% HTML 0.01% Rich Text Format 0.21%
xml xsd linq linqtoxsd csharp dotnet-core dotnet-standard2 codedom code-generation

linqtoxsdcore's Introduction

LinqToXsdCore

Introduction

This is a port of LinqToXsd to .NET Core. Most of what was in the original project is here, but built for .NET Core! For people who specifically need .NET Framework 3.5 and 4.0-4.5 support, please use the original code on the codeplex archive. There's also a legacy nuget package.

This .NET Core port itself requires .NET Core 2.1 or 3.1, but it can generate code that is compatible with .NET Framework 4.6.x and .NET Core 2.x.

Build Status Nuget

Get started

You can get started by reading the instructions here to use the CLI tool to generate code.

After you've generated code for a given XSD, you can include the generated code in a shipping app or library, so long as it has a reference to the XObjectsCore nuget package. Don't add the LinqToXsdCore nuget package to your shipping app or library! That's just a command line tool to generate code.

Release notes are here.

RELEASENOTES.md

Wait so what is this?

LinqToXsd was first released back in 2009, and it was billed then as a way of 'providing .NET developers with support for typed XML programming...LINQ to XSD enhances the existing LINQ to XML technology'.

Basically LinqToXsd generates code at design time, which models the structure of a XML document according, according to a W3C XML Schema (XSD). This allows a developer to program against the generated code, using strong types, where classes represents element definitions and class properties (i.e. getters and setters) represent attributes on XML elements.

Consider this XML:

<?xml version="1.0" encoding="UTF-8"?>
<purchaseOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:noNamespaceSchemaLocation="./Purchase Order.xsd">
    <general>
        <poNum>poNum0</poNum>
        <poDate>2006-05-04</poDate>
    </general>
    <order>
        <companyName>companyName0</companyName>
        <address>address0</address>
        <city>city0</city>
        <stateProv>stateProv0</stateProv>
        <zipCode>zipCode0</zipCode>
        <country>country0</country>
        <phone>phone0</phone>
        <fax>fax0</fax>
        <contactName>contactName0</contactName>
    </order>
</purchaseOrder>

To get the <city> element with regular LINQ to XML code, you'd write:

var purchaseOrderDoc = XDocument.Load("po.xml");
var orderElement = purchaseOrderDoc.Descendants(XName.Get("order"));
var cityElement = orderElement.Descendants(XName.Get("city"));

With LinqToXsd you can instead write:

var po = purchaseOrder.Load("po.xml");
var order = po.order;
var city = order.city;

The amount of code one writes to traverse an XML document is reduced as LinqToXsd builds a strongly-typed model for you through its code generator. This makes LinqToXsd incredibly helpful when dealing with XML data, especially if it comes with an accompanying XSD file, as most XML formats tend to do nowadays.

You can also use LinqToXsd to create XML documents programmatically using the object API:

var newPo = new PurchaseOrder();
newPo.order = new Order();
order.city = "city1";
// now save
newPo.Save("newPo.xml");

Even if there isn't a native XSD, you can infer an XSD from an existing XML file and speed up your development that way.

How does this compare to xsd.exe?

LinqToXsd, ends up providing something very similar to the C# code-generation facilities that xsd.exe provides. The main difference between the two is that LinqToXsd takes a code-generation, in-memory model and LINQ-only approach where as xsd.exe provides several legacy facilities such as XDR to XSD, XSD to DataSet, direct assembly generation, and can even do the reverse of what LinqToXsd does and generate an XSD from CLR types.

LinqToXsd also tries very closely to model XSD constraints and compositors (sequence, choice, all, substitution groups) and user defined types as much as possible, including simple and complex types, both named and anonymous. A key distinction is that LinqToXsd models XML elements and types with generated C# classes to build 'XML Objects', transposing XSD semantics in a CLR, object-oriented way. These XML objects inherit from the base class XTypedElement.

Essentially LinqToXsd generates an in memory model of the XSD schema as opposed to the classes that xsd.exe generates, which are closer to plain old C# objects (POCOs). This has the end result of making LinqToXsd a very powerful tool for modeling custom document markup languages, and preserving schema semantics in code.

To get a more technical explanation of what LinqToXsd provides, please see the wiki.

Things not supported in this .NET Core port

  • No assembly generation - due to a dependency on CodeDOM, no direct code-generation (i.e. emitting .DLL files) is supported. CodeDOM for .NET Core does not support this on any platform (even Windows).

  • Custom build action - the Visual Studio targets project (which allowed feeding an XSD and configuration file as a custom build action) has not been ported and there are no plans to do so at the moment. This was decided because the code generation utility can be installed as a global tool using dotnet. Regenerating code on build can be automated by adding a Visual Studio pre-build event (see instructions here).

License

This is licensed under the same license that the original LinqToXsd project was licensed under, which is the Microsoft Public License (MS-PL): https://opensource.org/licenses/MS-PL

linqtoxsdcore's People

Contributors

jods4 avatar kayoub5 avatar mamift avatar rvuistin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

linqtoxsdcore's Issues

Eager/deferred/lazy?

I'm interested in using this to model very large Xmls, but I'm concerned that the library is effectively eagerly reading all the data in, and will take enormous amounts of data.
Is it just a thin layer on top of XDocument? (which I think is largely deferred execution, i.e. IEnumberable<>) or is it a bit more eager?

error CS1061: 'object' does not contain a definition for 'TypeDefinition'

Using the latest release 3.3.2, I generated XMLSchema.xsd.cs from XMLSchema.xsd and encountered the following errors:

/XMLSchema.xsd.cs(2992,63,2992,77): error CS1061: 'object' does not contain a definition for 'TypeDefinition' and no accessible extension method 'TypeDefinition' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
/XMLSchema.xsd.cs(2995,77,2995,91): error CS1061: 'object' does not contain a definition for 'TypeDefinition' and no accessible extension method 'TypeDefinition' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
/XMLSchema.xsd.cs(666,63,666,77): error CS1061: 'object' does not contain a definition for 'TypeDefinition' and no accessible extension method 'TypeDefinition' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
/XMLSchema.xsd.cs(669,77,669,91): error CS1061: 'object' does not contain a definition for 'TypeDefinition' and no accessible extension method 'TypeDefinition' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
/XMLSchema.xsd.cs(8199,31,8199,53): warning CS0414: The field 'wildcard.namespaceDefaultValue' is assigned but its value is never used
/XMLSchema.xsd.cs(1180,31,1180,53): warning CS0414: The field 'any.namespaceDefaultValue' is assigned but its value is never used
    CompilerServer: server - server processed compilation - 073b94af-ae5c-4719-9276-3ea7b51bf348

The error seems similar to #10 (comment). You can repeat this by grabbing the XSD and running linqtoxsd on it:

wget https://www.w3.org/2001/XMLSchema.xsd

      /// <summary>
        /// <para>
        /// Occurrence: optional
        /// </para>
        /// </summary>
        public virtual object lang {
            get {
                XAttribute x = this.Attribute(langXName);
                return XTypedServices.ParseUnionValue(x, lang.TypeDefinition);  <-- error 
            }
            set {
                this.SetUnionAttribute(value, "lang", this, langXName, lang.TypeDefinition); <- error
            }
        }

Enum generation not working

I noticed that enum generation is not working when the restriction is an anonymous type of an attribute.

The atom.xsd will show the issue because the textType's type attribute is generated as a string instead of an enum.

Part of it is within XsdToTypesConverter.cs BuildProperty for attribute where name of the type is set to he name of the attribute

            XmlSchemaSimpleType schemaType = attribute.AttributeSchemaType;
            var qName = schemaType.QualifiedName;
            if (qName.IsEmpty) {
                qName = attribute.QualifiedName;

Then later in TypesToCodeDom.cs ProcessProperties enum is only generated if it is an anonymous type:

                    var typeRef = propertyInfo.TypeReference;
                    if (typeRef.IsEnum && string.IsNullOrEmpty(typeRef.Name))
                    {
                        typeRef.Name = $"{propertyInfo.PropertyName}s";
                        CreateNestedEnumType(typeRef);
                    }

Double includes are not ignored.

Hello,

According to the help function of the tool, when there are multiple includes/imports are detected, they only get included once. This doesn't seem to be working. Is there any flag I must set to get this to works?

I'm using 143 XSD-files (6,09MB) which end up in +/- 54MB of code. So I really need splitting up the files.

Implement generating a FileInfo property on generated types

Implement generating a FileInfo property on generated types (the ones that map to global elements or complex types or have static Load() methods).

Also add a new public static Load method; one that accepts a FileInfo parameter, which is also saved as an instance property on the generated type.

Example:

public partial class element {
	public FileInfo FileInfo { get; set; }
	// ...
	public static element Load(FileInfo fileInfo)
	{
		var element = XTypedServices.Load<element>(fileInfo.FullName);
		element.FileInfo = fileInfo;

		return element;
	}
}

Parsing error when nullable elements with empty value and "xsi:nil"=true attribute

It appears that the generated code for nullable property getter only inspects if the element exists. It fails when the element exists with attribute "xsi:nil"=true.

Example:
xsd: <xsd:element name="createDate" type="xsd:dateTime" minOccurs="0" nillable="true" />

Incoming xml:

<someType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
      <createDate xsi:nil="true"/>
 </someType>

When accessing the generated createDate property, it throws, as it finds the element, and tries to parse the empty value to DateTime.

As XDocument/XElement are not schema-aware, I'm not even sure which approach for solving this will be more appropriate - inspecting for the nil=true, or using the XElement.IsEmpty to detect the null value.

C# nullables

It's so good that you are carrying on this project, thanks! It is so much better than xsd.exe.

Now that C# has nullable references, do you think that you could add some support?

  1. I think this would need to be behind a command-line argument, because unsupported syntax or preprocessor directives would surface in older compiler version.

  2. The easy short-term support is to add #nullable disable at the top of generated files.
    This instructs the compiler that code inside this file is unaware of nullable references.
    It avoids issues when the generated C# file is included in a project that is null-aware by default.

  3. If possible, it would be awesome to have proper nullable annotations instead.
    You have all the required metadata for that and already do the work for value types.
    Reference types that are optional or part of a choice should be nullable.

Short discussion about the correctness of those annotations

Nullable references are tricky, esp. on complex types that can be built up.
A balance must be achieved between usefulness and correctness.

When an xml file conform to its xsd is read, the annotations are exact.
If not, I think one can accept incorrect nullable annotations on invalid data (garbage in -> garbage out).

About invalid data: reading the docs, I believe linqtoxsd should throw when accessing missing elements, but reading the code I believe it does not? Throwing would solve any discussion regarding the correctness of nullable annotations.

LinqToXsd also allows the creation / writing of xml files. This is more complicated since during construction the xml might not be valid. A required attribute will be null until the code sets its value.

I argue that it's more useful to have the annotation:

  • it statically prevent setting invalid element/attribute values.
  • it is easy to explain that reading an incomplete document during construction can result in uninitialized values even though the annotation says otherwise. One can say the "required" displayed in doc comments is just as wrong.

Again, this is assuming that linqtoxsd doesn't throw when reading a value that wasn't set (which would make this discussion useless).

Missing null check in a ToXTypedElement of XTypedServices.cs

In line 36 of XTypedServices.cs there is missing a check for whether xe is null for ToXTypedElement.

Null is checked in other ToXTypedElement methods but is missing in this one. For example, a check for null is in line 75:

        public static T ToXTypedElement<T>(XElement xe) where T : XTypedElement
        {
            if (xe == null)
            {
                return null;
            }

            T xoSubType = GetAnnotation<T>(xe);

Such a check should be added to line 36:

        public static XTypedElement ToXTypedElement(XElement xe, ILinqToXsdTypeManager typeManager, Type rootType,
            Type contentType)
        {
            
            XTypedElement rootElement = GetAnnotation(rootType, xe);

So that it is like this:

        public static XTypedElement ToXTypedElement(XElement xe, ILinqToXsdTypeManager typeManager, Type rootType,
            Type contentType)
        {
            
            if (xe == null)
            {
                return null;
            }

            XTypedElement rootElement = GetAnnotation(rootType, xe);

Null Reference Exception with ILinqToXsdTypeManager

Thanks for updating LINQToXsdCore. We used it to generate the C# file.
In my scenario, we have to create WCF Service Contract based on C# generated code (Because client requirement and they not converted to .Net Core). And I am getting error deep in ILinqToXsdTypeManager when I calling the service from WCF Client Code.

I am working with a large set of XSDs (40-50 XSDs, as which sent by chiefengineer) and XML message is created based on these XSDs. C# file is generated from the XSDs (LINQToXsdCore tool)

I coped this C# file and add it in .Net 4.8 Project and create the dll. Most of the unit tests are passing. I used this DLL and create WCF Service. DLL is used to create the OperationContract and custom arguments get passed (not an integer or string, it is custom classes)
In unit test (same solution, not with WCF Client ) , I read the valid XML ,XmlSerializer , Deserialize and pass it to OperationContract as arguments and Objects are fills with data from XML. All is well.

Now I run the WCF service in visual studio and call it from another Visual studio as a WCF client (same way as unit test). I am not referencing the service in project but creating the CustomChannelFactory and passing the config of the service. WCF Service is getting called and I can see it in the debugger and it hit OperationContract .
But Objects are NOT getting fill the xml data , as it is done in the unit test. I can see the raw XML coming through as content but objects are not getting filled. Those objects are getting Null Reference Exception. Untyped and Content have the XML which I sent. See blow.

Everything looks good , service OperationContract getting called but data is not getting filled,
Hope I explained well :) , and I am sure you see this error a thousand times. object are not getting filled but in Unit test , they are. Untyped and Content have the XML.

i am not sure what I am missing and how to debug it. WCF logs are not helpful.
Please let me know if you need more info.

From Unit Test
image

From Client:
image

Error :

" at Xml.Schema.Linq.XTypedServices.GetAnnotation(Type t, XElement xe)\r\n at Xml.Schema.Linq.XTypedServices.ToXTypedElement(XElement xe, ILinqToXsdTypeManager typeManager, Type rootType, Type contentType)\r\n at Xml.Schema.Linq.XTypedServices.ToXTypedElement[W,T](XElement xe, ILinqToXsdTypeManager typeManager)\r\n at aoc.efiling.ecf.extension.CoreFilingMessage.op_Explicit(XElement xe) in C:\_\AOC.eFiling.ecf.exchange.2.2.xsd.cs:line 211906\r\n at aoc.efiling.ecf.exchange.ReviewFiling.ReviewFilingRequestType.get_CoreFilingMessage() in C:\AOC.eFiling.ecf.exchange.2.2.xsd.cs:line 837459\r\n at aoc.efiling.ecf.exchange.ReviewFiling.ReviewFilingRequest.get_CoreFilingMessage() in C:*\AOC.eFiling.ecf.exchange.2.2.xsd.cs:line 838025"

at Xml.Schema.Linq.XTypedServices.GetAnnotation(Type t, XElement xe)
at Xml.Schema.Linq.XTypedServices.ToXTypedElement(XElement xe, ILinqToXsdTypeManager typeManager, Type rootType, Type contentType)
at Xml.Schema.Linq.XTypedServices.ToXTypedElement[W,T](XElement xe, ILinqToXsdTypeManager typeManager)
at oasis.legalxml.courtfiling.MessageReceiptMessage.MessageReceiptMessage.op_Explicit(XElement xe)

LinqToXsd global tool bug

When linqtoxsd gen is invoked and passed the file path to an XSD which has no configuration file that matches $"{filename}.config" and -a argument is also passed then no output code is generated. Work around: omit the -a flag.

XObjectsCore references CodeDom

XObjectsCore is a runtime requirement for the code generated by this project.

It feels wrong that it references System.CodeDom, which would be better not embedded into our applications.

Do you think it would be hard to correct the layering and have a CodeDom dependency for the code generation only?

Modify the LinqToXsd program such that it will emit one xml namespace to one corresponding c# file.

I have an xsd schema set that, when LinqToXsd is executed against the xsd schema set, generates a file that is over 803,000 lines in length. No, I can make it smaller, this is for handling court information. Unfortunately, LinqToXsd can generate the file, but Visual Studio in it's current production iteration cannot handle the resulting near 30M file. VS crashes routinely. Easy fix - generate a single c# file for each xml namespace. In my case this would result in about 40 files, all of which would be handled by VS easily.
Something like a -m switch would be perfect.
Otherwise - awesome tool! Saved me a bunch of work!

maxOccurs="unbounded", is not respected for elements in choice block

When adding a second Document element to a MsgHead element defined in the following schema:

	<element name="MsgHead">
		<complexType>
			<sequence>
				<element ref="mh:MsgInfo"/>
				<choice>
					<element ref="mh:Document" maxOccurs="unbounded"/>
					<element ref="mh:PatientReport" maxOccurs="unbounded">
						<annotation>
							<documentation>xxx</documentation>
						</annotation>
					</element>
				</choice>
				<element ref="ds:Signature" minOccurs="0"/>
			</sequence>
		</complexType>
	</element>

the previous entry is removed.

This is happening in .\LinqToXsdCore\XObjectsCore\API\ChoiceContentModelEntity.cs when a call is made to this.RemoveChoices (line 20).

The maxOccurs="unbounded" does not seem to be respected.

Generated code doesn't compile if an element is named Content

Hello,

I've got a naming collision when an element is named Content in the xsd file.

When I generate the model for the following xsd files:
https://www.rixml.org/images/docs/schemas/RIXML-2_5.xsd
https://www.rixml.org/images/docs/schemas/RIXML-Common-2_5.xsd
https://www.rixml.org/images/docs/schemas/RIXML-datatypes-2_5.xsd

With this config file:

<?xml version="1.0" encoding="utf-8"?>
<Configuration xmlns="http://www.microsoft.com/xml/schema/linq">
 <CodeGeneration>
   <SplitCodeFiles By="Namespace" />
 </CodeGeneration>
 <Namespaces>
   <Namespace DefaultVisibility="public" Schema="http://www.rixml.org/2017/9/RIXML" Clr="Pretrade.Api.PublicationExport.Services.Features.Attachment.Models.Rixml" />
   <Namespace DefaultVisibility="public" Schema="http://www.rixml.org/2017/9/RIXML-datatypes" Clr="Pretrade.Api.PublicationExport.Services.Features.Attachment.Models.Rixml.DataTypes" />
 </Namespaces>
 <Validation>
   <VerifyRequired>false</VerifyRequired>
 </Validation>
 <Transformation>
   <Deanonymize strict="false" />
 </Transformation>
</Configuration>
        var rixml = new Research
        {
            researchID = "my-id",
            createDateTime = DateTime.UtcNow,
            language = "eng",
            Product = new[]
            {
                new Product
                {
                    Content = new Content
                    {
                        Title = new Title
                        {
                            TypedValue = "My title"
                        }
                    }
                }
            }
        };

The model cannot be used due to the following errors:

  • Ambiguity between 'Product.Content' and 'Product.Content'
  • 'Content' : member names cannot be the same as their enclosing type

No globa:: prefix for BuildWrapperDictionary

Generates:

private static void BuildWrapperDictionary() {
    wrapperDictionary.Add(typeof(OurClientMatters.Models.OurClientMatters.Contracts.ReviewPeriod), typeof(global::OurClientMatters.Models.OurClientMatters.Contracts.periodType));
}

When it should be:

private static void BuildWrapperDictionary() {
    wrapperDictionary.Add(typeof(global::OurClientMatters.Models.OurClientMatters.Contracts.ReviewPeriod), typeof(global::OurClientMatters.Models.OurClientMatters.Contracts.periodType));
}

Nice to have: Load and Parse overloads with LoadOptions

Sometimes it is good to have the line info, etc. Alternatively, overload which takes XDocument or XElement.

Also, async variants would be appreciated too.

It is understood, that this functionality can be added in consumer code, that's why "nice to have".

Codegen bugs

These are a few bugs I noticed while testing my latest PR and you can reproduce them with the wss.xsd from tests.
I don't see how they would be linked to my changes, so I'm opening this issue.

  1. There are tons of enums in this XSD, but for some reason one causes an error. Class FieldDefinition has both a local enum IMEMode and a property with the same name, which of course is a C# error.

  2. Inside ViewDefinition, member Query hides an inherited member but is not declared with new, that triggers a C# warnings.
    Here again there are tons of new members in the codegen, not sure why this one is missed.

  3. Conversely, member ID of SharedFieldDefinition is declared new although it doesn't hide anything, which is also a C# warning.
    You'll notice that the backing XName field I created triggers the same warning but that's expected because it uses the same IsNew logic as the property itself.

Duplicate enums are not generated

There are a couple of bugs related to enum generation.

  1. When you have two anonymous enum types defined for attributes within different complex types, the enum only generates for a single one of these comples types (see the example in the attachment)
    SimpleSchema.zip

  2. When you have two anonymous enum types defined for elements within different complex types, the enum is not generated at all (please see the example in the attachment)
    SimpleSchemaElement.zip

Streaming?

Sorry, this might be more of a question than an issue.

Is there a way to use this library to parse or write in a streaming fashion?
I noticed the "Known issue and Desiderata" section in wiki include Streaming support based on forthcoming LINQ to XML support. :(

Basically, I'm gonna parse and create documents that have a small "frame" (root tag, a header/footer, a few wrapper tags) and a then small element that's repeated many times (50K+).

If we go back to raw Linq to XML, I suppose the technique would be:

Reading

  • Create an XmlReader
  • For anything on the path from root to "large" sections: use the reader manually :(
  • Anything outside (header/footer), as well as the repeated elements can be made into XElement with XElement.ReadFrom(reader).

Writing

  • Anything small and the repeated parts can be created as XElement
  • The path from the "large" container up to the root must be made out of XStreamingElement that lazily enumerate their contents when they're serialized.

Linq to XSD

I suppose that today:

  • When reading I could use the XTypedElement(XmlReader) ctor to parse XElements parts. Navigating to those parts would be up to my own code.
  • When writing I could create typed individual XElement. Building the XStreamingElement path from root to the large parts would be to my own code.

Am I correct?

Do you have any idea for a design that could completely merge streaming support into Linq To XSD that I could help implement?

Reduce XName lookups

Looking at the generated code, I'm a bit surprised that getters systematically use XName.Get().
When parsing files with hundreds if not thousands of repeated tags and attributes, it's a lot of unrequired calls, which are not free as they involve several thread-safe hashtables of weakreferences underneath.

I think it could be a fairly trivial code fix: simply introduce static variable for each element and attribute name, and use that instead of the XName.Get() expression in the code.

Impact analysis of the change

One could be concerned about overall memory usage (when parsing), leaks (XName uses WeakRef and releases namespaces and names once they're not referenced anymore), cost for elements that are not accessed (e.g. large xsd with only a tiny subset accessed at runtime), and initialization time.

For elements, there is no impact at all.
The static ctor already creates every XName and saves it in localElementDictionary and sometimes in contentModel as well.
So the work to instantiate the XNames is already done anyway and they will be kept alive forever.

Attributes are created lazily on first access. They are kept alive forever though, as only full namespaces are collected when unused, not individual names.
Given all the work done anyway on elements, I don't think adding the attributes is a significant change. It should be an all-or-nothing design.
If that's really a concern, it could be done on elements only.

So in summary this change would:

  • keep static initialization time constant.
  • keep overall memory usage the same.
  • reduce code/IL size.
  • speed-up runtime access.

Alternate Enum Generation strategy

After the enum generation is completed I would be willing to make this change.

I have to work with XML files provided by vendors that define the enumeration restriction for fields that change somewhat often. This issue is what lead me to find LinqToXsdCore. When an xml enumeration is compiled into a .net enum, it cannot handle XML that comes across with a different value than specified in the schema.

Semantically, the schema says it is invalid. Treating enums as strings solves this, but the reality is that the generated enums are really helpful when producing input to a vendor. They may be useful when consuming a document, but also may never be looked at again.

I like LinqToXsdCore because I can load a document, look at only the sections I need, and save it. If the vendor added entirely new elements without telling me, it works fine in the round-trip.

I can't be the only one that's had this kind of issue with the strict enums and vendor controlled formats. I thought it might be worth while to look into a flexible enumeration strategy instead of compiled enums.

See: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/enumeration-classes-over-enum-types

This defines a Enumeration that is integral with a name (just like regular enums), but for many XML restrictions it probably doesn't even need the integral part.

Code snippets from the above link:

public abstract class Enumeration : IComparable
{
    public string Name { get; private set; }

    public int Id { get; private set; }

    protected Enumeration(int id, string name)
    {
        Id = id;
        Name = name;
    }

    public override string ToString() => Name;

    public static IEnumerable<T> GetAll<T>() where T : Enumeration
    {
        var fields = typeof(T).GetFields(BindingFlags.Public |
                                         BindingFlags.Static |
                                         BindingFlags.DeclaredOnly);

        return fields.Select(f => f.GetValue(null)).Cast<T>();
    }

    public override bool Equals(object obj)
    {
        var otherValue = obj as Enumeration;

        if (otherValue == null)
            return false;

        var typeMatches = GetType().Equals(obj.GetType());
        var valueMatches = Id.Equals(otherValue.Id);

        return typeMatches && valueMatches;
    }

    public int CompareTo(object other) => Id.CompareTo(((Enumeration)other).Id);

    // Other utility methods ...
}

public class CardType : Enumeration
{
    public static readonly CardType Amex = new CardType(1, "Amex");
    public static readonly CardType Visa = new CardType(2, "Visa");
    public static readonly CardType MasterCard = new CardType(3, "MasterCard");

    public CardType(int id, string name)
        : base(id, name)
    {
    }
}

I was thinking about just post-processing the code dom. But after looking into the get/set accessors I decided if I can get this feature to be properly supported in this library that would be much easier.

Replicate Codeplex Command Line

Below is my old command line to generate one CS. I tried with gen -c and it generated 2 CS.

How do I do the same with LinqToXsdCore? I only want 1 CS from 4 XSD files.

c:\LinqToXsdBin\LinqToXsd.exe APIDataTypes.xsd APIEvents.xsd APIEventTypes.xsd APIResponse.xsd /fileName:OutputFile.cs

Wrong generated code for union property.

You can reproduce the problem with the NoVoidTypeOfExpressionsInGeneratedCodeEver test and the Schemas\XSD\W3C XMLSchema v1.xsd schema.

Generated code:
return XTypedServices.ParseUnionValue(x, void.TypeDefinition);
instead of:
return XTypedServices.ParseUnionValue(x, null);

Happens in CreateFieldReference when typeName is an empty string instead of a null value:

 	XObjectsCore.dll!Xml.Schema.Linq.CodeGen.CodeDomHelper.CreateFieldReference(string typeName, string fieldName) Line 465	C#
 	XObjectsCore.dll!Xml.Schema.Linq.CodeGen.ClrPropertyInfo.GetSimpleTypeClassExpression() Line 1251	C#
>	XObjectsCore.dll!Xml.Schema.Linq.CodeGen.ClrPropertyInfo.AddGetStatements(System.CodeDom.CodeStatementCollection getStatements) Line 1020	C#
 	XObjectsCore.dll!Xml.Schema.Linq.CodeGen.ClrPropertyInfo.AddToType(System.CodeDom.CodeTypeDeclaration parentTypeDecl, System.Collections.Generic.List<Xml.Schema.Linq.CodeGen.ClrAnnotation> annotations, Xml.Schema.Linq.GeneratedTypesVisibility visibility) Line 674	C#
 	XObjectsCore.dll!Xml.Schema.Linq.CodeGen.TypePropertyBuilder.GenerateCode(Xml.Schema.Linq.CodeGen.ClrBasePropertyInfo property, System.Collections.Generic.List<Xml.Schema.Linq.CodeGen.ClrAnnotation> annotations) Line 30	C#
 	XObjectsCore.dll!Xml.Schema.Linq.CodeGen.XTypedElementBuilder.CreateAttributeProperty(Xml.Schema.Linq.CodeGen.ClrBasePropertyInfo propertyInfo, System.Collections.Generic.List<Xml.Schema.Linq.CodeGen.ClrAnnotation> annotations) Line 782	C#

Bug: optional enums

Generated code doesn't correctly take into account optional enums.

See this excerpt generated from ISO 20022 pain.002.001.03.xsd

        /// <summary>
        /// <para>
        /// Occurrence: optional
        /// </para>
        /// <para>
        /// Regular expression: (StsId?, OrgnlInstrId?, OrgnlEndToEndId?, TxSts?, StsRsnInf*, ChrgsInf*, AccptncDtTm?, AcctSvcrRef?, ClrSysRef?, OrgnlTxRef?)
        /// </para>
        /// </summary>
        public virtual TransactionIndividualStatus3Code TxSts {
            get {
                XElement x = this.GetElement(System.Xml.Linq.XName.Get("TxSts", "urn:iso:std:iso:20022:tech:xsd:pain.002.001.03"));
                return ((TransactionIndividualStatus3Code)(Enum.Parse(typeof(TransactionIndividualStatus3Code), XTypedServices.ParseValue<string>(x, XmlSchemaType.GetBuiltInSimpleType(XmlTypeCode.String).Datatype))));
            }
            set {
                this.SetElementWithValidation(System.Xml.Linq.XName.Get("TxSts", "urn:iso:std:iso:20022:tech:xsd:pain.002.001.03"), value.ToString(), "TxSts", global::Iso20022.Pain_002_001_03.TransactionIndividualStatus3CodeValidator.TypeDefinition);
            }
        }

You can see in the comments that this field is correctly described as Occurence: optional and Regex: (... TxSts? ...).

Yet the property type is a non-nullable Enum TransactionIndividualStatus3Code.

If you try to read its value, you don't get 0 but an ArgumentNullException because it calls Enum.Parse passing null as the string value (from XTypedServices.ParseValue<string>).

I think a fix would need to:

  • Change the return type to a nullable enum.
  • Check if the value exists before calling Enum.Parse and return null instead.

Bonus chatter: I am not sure what runtime you support. If you can go with .net core 2.0 / .net standard 2.1 then there's a handy generic Enum.Parse<> that was added.

Publish a Nuget package

I am very much interested in using this project but I don't seem to be able to find a nuget package for it.

Impossible to set nullable properties to null

The generated setter for a nullable DateTime looks like this:

        public virtual System.Nullable<System.DateTime> RltdDt {
            get { /* ... */ }
            set {
                this.SetElementWithValidation(System.Xml.Linq.XName.Get("RltdDt", "urn:iso:std:iso:20022:tech:xsd:pain.002.001.03"), value, "RltdDt", global::Iso20022.Pain_002_001_03.ISODate.TypeDefinition);
            }
        }

Trying to remove such an element by setting it to null fails with an exception.
The setter calls SetElementWithValidation with value null and the validation first performed by this method fails, although the code after validation would correctly remove the element upon receiving a null value.

Notice that the situation is slightly worse for an enum, because the generated code passes value.ToString() to SetElementWithValidation and that .ToString() would fail even earlier.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.