I had a class similar to this:
public class Script
{
public string Name { get; set; }
public string HomeKey { get; set; } = "";
}
which would deserialize from an XML file like this:
<Script>
<Name>ScriptName</Name>
<HomeKey>KeyText</HomeKey>
</Script>
This works fine using the following code:
[Test]
public void DeserializeScriptTest()
{
// Arrange
var contents = Helpers.LoadEmbeddedResourceFileContents("test.xml");
var serializer = new XmlSerializer(typeof(Script));
Script s;
// Act
using (var reader = new StringReader(contents))
{
s = (Script)serializer.Deserialize(reader);
}
// Assert
Assert.AreEqual("KeyText", s.HomeKey);
}
And I have lots of client code out there that uses these files and will continue to use them into the future.
Now my XML file has changed slightly so that it looks like this:
<Script>
<Name>ScriptName</Name>
<HomeKey KeyId="12345">KeyText</HomeKey>
</Script>
My old clients should still be able to deserialize this file as the old HomeKey is still in the same place. New clients need to deserialize the attribute value too. So I created a new class:
public class ScriptV2 : Script
{
public new HomeKeyV2 HomeKey { get; set; } = new HomeKeyV2();
}
public class HomeKeyV2
{
[XmlAttribute]
public int KeyId { get; set; }
[XmlText]
public string Value { get; set; } = "";
}
The old code works with the new files just fine. However, the new code doesn't work.
When I run this:
[Test]
public void DeserializeScriptV2Test()
{
// Arrange
var contents = Helpers.LoadEmbeddedResourceFileContents("test.xml");
var serializer = new XmlSerializer(typeof(ScriptV2), new XmlRootAttribute("Script")); // <-- Exception here
ScriptV2 s;
// Act
using (var reader = new StringReader(contents))
{
s = (ScriptV2)serializer.Deserialize(reader);
}
// Assert
Assert.AreEqual("KeyText", s.HomeKey.Value);
}
I get an exception on the marked line:
System.InvalidOperationException
HResult=0x80131509
Message=There was an error reflecting type 'Project.Classes.ScriptV2'.
Source=System.Xml
StackTrace:
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)
at System.Xml.Serialization.XmlReflectionImporter.ImportElement(TypeModel model, XmlRootAttribute root, String defaultNamespace, RecursionLimiter limiter)
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type, XmlRootAttribute root, String defaultNamespace)
at System.Xml.Serialization.XmlSerializer..ctor(Type type, XmlAttributeOverrides overrides, Type[] extraTypes, XmlRootAttribute root, String defaultNamespace, String location, Evidence evidence)
at System.Xml.Serialization.XmlSerializer..ctor(Type type, XmlRootAttribute root)
at Project.Tests.DeserializeScriptV2Test() in foobarTests.cs:line 12
Inner Exception 1:
InvalidOperationException: There was an error reflecting property 'HomeKey'.
Inner Exception 2:
InvalidOperationException: Member ScriptV2.HomeKey of type Project.Classes.HomeKeyV2 hides base class member Script.HomeKey of type System.String. Use XmlElementAttribute or XmlAttributeAttribute to specify a new name.
How can I change ScriptV2 so that it works with the new structure of the xml file? I can't change Script.cs or deploy updated code to the old clients. They are all working fine with the altered XML files.
This works for me
[XmlRoot(ElementName = "HomeKey")]
public class HomeKey
{
[XmlAttribute(AttributeName = "KeyId")]
public string KeyId { get; set; }
[XmlText]
public string Text { get; set; }
}
[XmlRoot(ElementName = "Script")]
public class ScriptV2
{
[XmlElement(ElementName = "Name")]
public string Name { get; set; }
[XmlElement(ElementName = "HomeKey")]
public HomeKey HomeKey { get; set; }
}
Usage
var contents = "<Script>\r\n <Name>ScriptName</Name>\r\n <HomeKey KeyId=\"12345\">KeyText</HomeKey>\r\n</Script>";
var serializer = new XmlSerializer(typeof(ScriptV2), new XmlRootAttribute("Script"));
using (var reader = new StringReader(contents))
{
var s = (ScriptV2)serializer.Deserialize(reader);
}
User contributions licensed under CC BY-SA 3.0