.Net XmlSerializer: Parse an xml in relaxed mode, continue on error

Simone Demuro 50 Reputation points
2023-06-15T10:46:18.57+00:00

Hello,

I wondered if with XmlSerializer, is it possible to continue the XML deserialization of an XML in case of errors with some properties.

Given a well-formed but invalid XML. Defining invalid as an XML with an illegal value, for example, Date in an incorrect format.

Example:

[XmlRoot(ElementName = "root")]
public class Root
{
	[XmlElement(ElementName = "intField")]
	public int IntField { get; set; }

	[XmlElement(ElementName = "strField")]
	public string StrField { get; set; }
}
<root>    
  <intField>ciao</intField>    
  <strField>hello</strField> 
</root>

In this case, I'd expect to have a way to deserialize the above XML in an object that will contain a correctly parsed strField, and intField with the default of 0.

Working with Jsons with Newtonsoft such a thing is possible using the Error callback of JsonSerializerSettings, like follow:

DeserializeObject<List<Something>>(applesJson, new JsonSerializerSettings()
{
     Error = (sender, error) => error.ErrorContext.Handled = true
});

Decompiling the Deserialize I found the XmlDeserializationEvents and I tried to register all, but none of them fired on type mismatch.

	//e.OnUnknownAttribute += On_UnknownAttribute;
	//e.OnUnknownElement += On_UnknownElement;
	//e.OnUnknownNode += On_UnknownNode;
	//e.OnUnreferencedObject += On_UnreferencedObject;

I also tried to use the XmlReaderSettings to specify a ValidationEventHandler, but again it did not fire.

Here is some code that might save some time in case anybody wants to try something more:

[XmlRoot(ElementName = "root")]
public class Root
{
	[XmlElement(ElementName = "intField")]
	public int IntField { get; set; }

	[XmlElement(ElementName = "strField")]
	public string StrField { get; set; }
}

void Main()
{
	var xmlText = @"<root><intField>ciao</intField><strField>hello</strField></root>";

	XmlSerializer serializer = new(typeof(Root));
	using StringReader strReader = new(xmlText);
	using XmlReader xmlReader = XmlReader.Create(strReader);

	var test = (Root)serializer.Deserialize(xmlReader);
	// test.Dump();
}

// You can define other methods, fields, classes and namespaces here

Surfing the web with this question is easy to see how this question is very common but I have found no answer for it.

Thanks & Happy coding!

Developer technologies C#
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Viorel 122.5K Reputation points
    2023-06-15T18:10:36.0533333+00:00

    Sometimes you can add a special property:

    [XmlRoot( ElementName = "root" )]
    public class Root
    {
        [XmlIgnore]
        public int IntField { get; set; }
    
        [XmlElement( ElementName = "intField" )]
        public string IntFieldInternal
        {
            get { return IntField.ToString( ); }
            set { int.TryParse( value, out int i ); IntField = i; }
        }
    
        [XmlElement( ElementName = "strField" )]
        public string StrField { get; set; }
    }
    
    

  2. Simone Demuro 50 Reputation points
    2023-06-15T18:27:39.7333333+00:00

    Thanks for your answer!

    This approach work, but on a larger scale where you generate the models automatically, with paste special from Visual studio or https://json2csharp.com/code-converters/xml-to-csharp .
    This would require writing a source generator, do you know if there is an existing one with such an incapsulation feature?

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.