Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ignore unknown types when deserializing xml

I've got this code:

[XmlType( "Metadata" )]
[Serializable]
public class MetadataContainer : List<MetadataBase>
{
}

[XmlType( "Meta" )]
[XmlInclude( typeof( ReadonlyMetadata ) )]
[Serializable]
public abstract class MetadataBase
{
}

[XmlType( "Readonly" )]
[Serializable]
public class ReadonlyMetadata : MetadataBase
{
}

[TestFixture]
public class SerializationTests
{
    [Test]
    public void Can_deserialize_with_known_type()
    {
        const string text = @"<Metadata xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
                        <Meta xsi:type=""Readonly"" />
                    </Metadata>";

        var serializer = new XmlSerializer( typeof( MetadataContainer ) );
        var metas = (MetadataContainer)serializer.Deserialize( XmlReader.Create( new StringReader( text ) ) );

        Assert.That( metas.Count, Is.EqualTo( 1 ) );
        Assert.That( metas.First(), Is.InstanceOf<ReadonlyMetadata>() );
    }

    [Test]
    public void Can_deserialize_with_unknown_type()
    {
        const string text = @"<Metadata xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
                        <Meta xsi:type=""Hello"" />
                    </Metadata>";

        var serializer = new XmlSerializer( typeof( MetadataContainer ) );
        var metas = (MetadataContainer)serializer.Deserialize( XmlReader.Create( new StringReader( text ) ) );

        Assert.That( metas.Count, Is.EqualTo( 0 ) );
    }
}

The first test works, but when I run the second I get this error:

System.InvalidOperationException : There is an error in XML document (2, 9). ----> System.InvalidOperationException : The specified type was not recognized: name='Hello', namespace='', at .

Instead of getting this error I would like it to ignore not recognized types. Is there any way to do this?

like image 277
Allrameest Avatar asked Oct 30 '12 10:10

Allrameest


People also ask

Can I make XmlSerializer ignore the namespace on Deserialization?

Yes, you can tell the XmlSerializer to ignore namespaces during de-serialization. Note this is the kind of thing I meant. You are not telling the XmlSerializer to ignore namespaces - you are giving it XML that has no namespaces.

What is the correct way of using XML Deserialization?

XML Serialization Considerations Type identity and assembly information are not included. Only public properties and fields can be serialized. Properties must have public accessors (get and set methods). If you must serialize non-public data, use the DataContractSerializer class rather than XML serialization.

What is Deserializing XML?

Deserialization is the process of reading an XML document and constructing an object that is strongly typed to the XML Schema (XSD) of the document. Before deserializing, an XmlSerializer must be constructed using the type of the object that is being deserialized.

Which class is used for serializing and deserializing XML data?

XmlSerializer Class (System. Xml. Serialization)


1 Answers

Generic solution for similar problems:

Have a look at unknown element event (link) and unknown attribute event (link) and see if they solve the problems, or we have to get dirty. Read on...

Working solution for this problem

Bear in mind that that I have no idea what your task is, AFAIK it is serializing xml into your datastructure. If you can change the datastructure I would recommend you to have a look at Linq2XML and create a smart factory for your purposes.

[TestMethod]
public void TestLinq2Xml()
{
  const string text = @"<Metadata xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
                            <Meta xsi:type=""Readonly"" />
                            <Meta xsi:type=""Garbage"" />
                      </Metadata>";

  // Get the "names" of all implementors of MetadataBase
  var types = AppDomain.CurrentDomain.GetAssemblies().ToList()
     .SelectMany(s => s.GetTypes())
         .Where(p => typeof(MetadataBase).IsAssignableFrom(p) && !p.IsAbstract && !p.IsInterface)
         .Where(t => t.GetCustomAttributes(typeof(XmlTypeAttribute), false).Any())
         .Select(t => t.GetCustomAttributes(typeof(XmlTypeAttribute), false)
             .Cast<XmlTypeAttribute>().First().TypeName);

  // Create a parser
  var parser = new XmlSerializer(typeof(MetadataBase));

  // Create metadatacontainer to fill
  var metas = new MetadataContainer();
  // Fill it with matching from from the XML
  metas.AddRange((from t in XDocument.Parse(text).Descendants("Meta")
                where types.Contains(t.Attribute(XName.Get("type", "http://www.w3.org/2001/XMLSchema-instance")).Value)
                select (MetadataBase)parser.Deserialize(t.CreateReader())).ToList());

  // Should be one guy present
  Assert.AreEqual(metas.Count, 1);
}
like image 58
flindeberg Avatar answered Nov 15 '22 09:11

flindeberg