Home > other >  C# XML Deserialization where node attribute is case-insensitive even though xml is case sensitive
C# XML Deserialization where node attribute is case-insensitive even though xml is case sensitive

Time:02-11

I am trying to read PackageReferences from an XML document.

My xml files look something like following:

<Project>
  <ItemGroup>
    <PackageReference Update="PackageName" Version="PackageVersion" />
  </ItemGroup>
</Project>

AND

<Project>
  <ItemGroup>
    <PackageReference Update="PackageName" version="PackageVersion" />
  </ItemGroup>
</Project>

(First example has a capital V in "Version" and the second example has a lowercase v in "version")

I know that XML is case sensitive. Unfortunately, the customers submitting the xml files for processing often make this casing mistake. Due to the number of customers and manual edits to these similar files, it's very difficult to keep this in line. What are my options to have my C# program safely ignore casing? Currently my program only captures values with the capital V in 'Version' and puts a null down for the lowercase 'version' examples. I still need the PackageVersion value in both cases.

Here is my code:

Reader.cs:

var xmlResolver = new XmlSecureResolver(new XmlUrlResolver(), new System.Security.PermissionSet(System.Security.Permissions.PermissionState.None));
using (var sr = new StringReader(this.FileSystem.File.ReadAllText(path)))
{
  var textReader = new XmlTextReader(sr) { Namespaces = false, XmlResolver = null };
  var serializer = new XmlSerializer(typeof(Project));
  return (Project)serializer.Deserialize(textReader);
}

Project.cs partial class:

{
    using System;
    using System.Xml.Serialization;

    [Serializable]
    public partial class Project
    {
        private ProjectItemGroup[] itemGroupField;

        [XmlElement("ItemGroup")]
        public ProjectItemGroup[] ItemGroup
        {
            //...
        }
    }

    [Serializable]
    public partial class ProjectItemGroup
    {
        private Package[] packageReferenceField;

        [XmlElement("PackageReference")]
        public Package[] PackageReference
        {
            get
            {
                return this.packageReferenceField;
            }
            set
            {
                this.packageReferenceField = value;
            }
        }
    }

}

Package.cs

    [Serializable]
    public class Package
    {
        [XmlAttribute(AttributeName = "Update")]
        public string Id { get; set; }

        [XmlAttribute(AttributeName = "Version")]
        public string Version { get; set; }
    }

I looked through all the properties documentation here: https://docs.microsoft.com/en-us/dotnet/api/system.xml.xmltextreader.namespaces?view=net-6.0 and couldn't see a way to ignore case. (Earlier, I was able to ignore a namespace attribute on the root node as described here: C# XML Deserialization where root node sometimes has namespace attribute)

The options I potentially see are to lowercase the full document and re-write the program to expect all lowercase values. Another option might be to also add a lowercase field for version and somehow combine the results for uppercase Version and lowercase version. Is there something less hacky I am missing?

CodePudding user response:

Thanks @canton7 for your idea to subclass XmlTextReader overriding LocalName. I ended up making the following changes to Reader.cs:

public class CaseInsensitiveXmlTextReader : XmlTextReader
{
  public CaseInsensitiveXmlTextReader(System.IO.TextReader reader) : base(reader) { }
  public override string LocalName
  {
    get
    {
      if (String.Equals(base.LocalName, "version", StringComparison.Ordinal))
      {
        return "Version";
      }
      else
      {
        return base.LocalName;
      }
    }
  }
}

And then I updated the textReader to use the CaseInsensitiveXmlTextReader:

var textReader = new CaseInsensitiveXmlTextReader(sr) { Namespaces = false, XmlResolver = xmlResolver };
  • Related