0

We have a Windows Store application that communicates with our server using XML for requests / responses and are serialized with the XmlSerializer. The issue we are encountering is that one of our types can contain arbitrary XML as one of its properties. In non WinRT applications, the usage would have been.

public sealed ItemExtension {
    [XmlAttribute("source")]
    public string Source {get;set;}

    [XmlAnyElement]
    public XmlElement[] Data {get;set;}
}

This would allow us to have XML in our database like

<extension source="foo"><randomXml><data/></randomXml></extension>

In WinRT, XmlElement is not included, System.Xml.XmlElement does not exist and the Windows.Data.Xml.Dom.XmlElement is not compatible. Documentation mentions XElement, but XElement is not a supported WinRT type so the WinRT project won't compile if I try using it.

Is this a bug with Windows Store applications or is there a sufficient work around?

Thanks.

littlepod
  • 1
  • 1
  • If you use `[XmlAnyElement] public object[] Data { get; set; }`, does that work? – WiredPrairie Jan 21 '14 at 22:50
  • Yes this works for the deserialization. I got that working a little bit ago, and it's weird... so in the debugger, when I deserialize existing data, it'll be of type System.Xml.XmlElement. I can then do some hackery, to use the deserializer, to instantiate XmlElement to save new data. The problem though is that to actually read/use the data, it's impossible. System.Xml.XmlElement doesn't exist in WinRT even though it works great in the debugger. If I pass it off to the javascript layer, an exception gets thrown and since I can't see System.Xml.XmlElement, I can't cast / use it in anyway. – littlepod Jan 22 '14 at 00:45
  • I then tried to use reflection and just call the OuterXml property and pass a string back. PropertyInfo property = Data.GetType().GetRuntimeProperty("OuterXml"); var xmlString = property.GetValue(Data) as String; return xmlString; This throws an exception "The API 'System.Xml.XmlNode.get_OuterXml()' cannot be used on the current platform" Not sure what outs I have left now. – littlepod Jan 22 '14 at 00:48
  • Okay I got it working, the same hackery to deserialize, I can use that again and reserialize an XmlElement to a string and that outputs the original Xml... I wish there was a better way to do this. – littlepod Jan 22 '14 at 00:51
  • Are you locked into using the `XmlSerializer`? – WiredPrairie Jan 22 '14 at 01:05
  • At this point yes... as our platform is aged and works around XML for our web requests and responses and all of our types/classes are already xml attributed. We'll look into DataContractSerializer, or better yet going towards JSON in the future, but for now we're stuck. – littlepod Jan 22 '14 at 01:38
  • Good luck then. The XmlSerializer in WinRT seems busted. (I noticed the same behavior you're seeing with the odd types that aren't quite there when I tested your code after making the suggestion about `object[]`) – WiredPrairie Jan 22 '14 at 01:48

1 Answers1

0

So far I've only found a hack to get this working. If we use

[XmlAnyElement]
public object Data {get;set;}

This will properly will properly deserialize existing data. In the debugger when inspecting it, it is of type System.Xml.XmlElement, which isn't exposed in WinRT. So there's no way to directly set this. Since we figured out that XmlSerializer can instantiate and access System.Xml.XmlElement, we use it to handle setting it by taking in an object/xml snippet, wrapping it in container xml for a wrapper type that contains [XmlAnyElement] and calling Deserialize on it to have the XmlSerializer instantiate an XmlElement, which can then be set on the target object you wish to serialize.

For getting data, since trying to read this property throws an exception in the UI layer, and trying to access InnerXml/OuterXml throw an exception as well, we are left with using the XmlSerializer to Serialize the XmlElement back into a string, and then can use that however you want.

public sealed class XmlAnyElementContainer
{
    [XmlAnyElement]
    public object Data { get; set; }
}

public void SetData(object extensionObject)
{
    var objectSerializer = new XmlSerializer(extensionObject.GetType());

    var settings = new XmlWriterSettings()
    {
        Indent = false,
        OmitXmlDeclaration = true
    };

    var sb = new StringBuilder();
    using (var xmlWriter = XmlWriter.Create(sb, settings))
    {
        objectSerializer.Serialize(xmlWriter, extensionObject);
    }

    string objectXml = sb.ToString();

    string newXml = "<XmlAnyElementContainer>" + objectXml + "</XmlAnyElementContainer>";
    var xmlAnySerializer = new XmlSerializer(typeof(XmlAnyElementContainer));
    using (var sr = new StringReader(newXml))
    {
        [TargetPropertyToSerialize] = (xmlAnySerializer.Deserialize(sr) as XmlAnyElementContainer).Data;
    }
}
littlepod
  • 1
  • 1