Haydn
Haydn

Reputation: 303

Xml Deserialize missing strings

I am deserializing an object from xml, but the strings in KeywordList and ResponseList always end up empty.

Tried lots of things and can't seem to get it right. If I loop through it with a reader then the values show up.

using System;
using System.Collections.Specialized;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(ElizaResponses));
            using (XmlReader reader = XmlReader.Create("eliza.xml"))
            {
                ElizaResponses responses = (ElizaResponses)serializer.Deserialize(reader);
                reader.Close();

                // This reads in all data
                //StreamReader reader = new StreamReader("eliza.xml");
                //reader.ReadToEnd();
                //reader.MoveToContent();
                //while (reader.Read())
                //{
                //    if(reader.NodeType == XmlNodeType.Text)
                //    {
                //        Console.WriteLine("{0}, {1}", reader.NodeType, reader.Value);
                //    }
                //    else
                //    { 
                //    Console.WriteLine("{0}, {1}", reader.NodeType, reader.Name);
                //    }
                //}


            }

            Console.ReadLine();
        }
    }

    [Serializable]
    [XmlRoot("ElizaResponses")]
    [XmlType("ElizaResponses")]
    public sealed class ElizaResponses
    {
        ElizaConversation[] chatList;

        public ElizaConversation[] ChatList
        {
            get { return this.chatList; }
            set { this.chatList = value; }
        }
    }

    [Serializable]
    public sealed class ElizaConversation
    {
        // Tried List<string>, string[], StringCollection
        StringCollection keywordList = new StringCollection();
        StringCollection responseList = new StringCollection();

        // Tried [XmlArray], [XmlElement]
        [XmlElement("KeywordList")]
        [XmlArrayItem("string", typeof(string))]
        StringCollection KeywordList
        {
            get { return this.keywordList; }
            set { this.keywordList = value; }
        }

        [XmlArray("ResponseList")]
        [XmlArrayItem("string", typeof(string))]
        StringCollection ResponseList
        {
            get { return this.responseList; }
            set { this.responseList = value; }
        }
    }
}

Here is the example xml

<?xml version="1.0" encoding="utf-8"?>
<ElizaResponses>
  <ChatList>
    <ElizaConversation>
      <KeywordList>
        <string>ok</string>
      </KeywordList>
      <ResponseList>
        <string>cool.</string>
        <string>great.</string>
        <string>:)</string>
      </ResponseList>
    </ElizaConversation>
    <ElizaConversation>
      <KeywordList>
        <string>shutup</string>
        <string>shut up</string>
      </KeywordList>
      <ResponseList>
        <string>make me.</string>
        <string>no I won't.</string>
      </ResponseList>
    </ElizaConversation>
  </ChatList>
</ElizaResponses>

Should add I get no exceptions and have turned up the error messaging in the app.config file

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
    </startup>
  <system.diagnostics>
    <switches>
      <add name="XmlSerialization.Compilation" value="4" />
    </switches>
  </system.diagnostics>
</configuration>

Upvotes: 0

Views: 136

Answers (2)

dbc
dbc

Reputation: 116785

Rather than continuing to attempt to hand-generate your types, you should start by auto-generating them and verifying the auto-generated types work. Only then, simplify as needed. If you post your XML to https://xmltocsharp.azurewebsites.net/, you can generate classes that will deserialize your XML:

[XmlRoot(ElementName = "KeywordList")]
public class KeywordList
{
    [XmlElement(ElementName = "string")]
    public List<string> String { get; set; }
}

[XmlRoot(ElementName = "ResponseList")]
public class ResponseList
{
    [XmlElement(ElementName = "string")]
    public List<string> String { get; set; }
}

[XmlRoot(ElementName = "ElizaConversation")]
public class ElizaConversation
{
    [XmlElement(ElementName = "KeywordList")]
    public KeywordList KeywordList { get; set; }
    [XmlElement(ElementName = "ResponseList")]
    public ResponseList ResponseList { get; set; }
}

[XmlRoot(ElementName = "ChatList")]
public class ChatList
{
    [XmlElement(ElementName = "ElizaConversation")]
    public List<ElizaConversation> ElizaConversation { get; set; }
}

[XmlRoot(ElementName = "ElizaResponses")]
public class ElizaResponses
{
    [XmlElement(ElementName = "ChatList")]
    public ChatList ChatList { get; set; }
}

You can verify using your code above that this version of ElizaResponses will deserialize your XML. However, these can be simplified.

Firstly, merge KeywordList and ResponseList into StringList:

public class StringList
{
    [XmlElement(ElementName = "string")]
    public List<string> String { get; set; }
}

[XmlRoot(ElementName = "ElizaConversation")]
public class ElizaConversation
{
    [XmlElement(ElementName = "KeywordList")]
    public StringList KeywordList { get; set; }
    [XmlElement(ElementName = "ResponseList")]
    public StringList ResponseList { get; set; }
}

Next, eliminate StringList entirely replacing it with List<string<>. Since an extra level of container class is being eliminated, the two [XmlElement] attributes will need to replaced with [XmlArray] and [XmlArrayItem] resulting in the following final versions:

[XmlRoot(ElementName = "ElizaConversation")]
public class ElizaConversation
{
    [XmlArray(ElementName = "KeywordList")]
    [XmlArrayItem(ElementName = "string")]
    public List<string> KeywordList { get; set; }
    [XmlArray(ElementName = "ResponseList")]
    [XmlArrayItem(ElementName = "string")]
    public List<string> ResponseList { get; set; }
}

[XmlRoot(ElementName = "ChatList")]
public class ChatList
{
    [XmlElement(ElementName = "ElizaConversation")]
    public List<ElizaConversation> ElizaConversation { get; set; }
}

[XmlRoot(ElementName = "ElizaResponses")]
public class ElizaResponses
{
    [XmlElement(ElementName = "ChatList")]
    public ChatList ChatList { get; set; }
}

Your initial attempt included the following problems:

  • The non-generic, obsolete StringCollection should be avoided.
  • In the [XmlArrayItem("Item", typeof(string))] declarations, the name "Item" is wrong. It should have been "string".
  • [XmlElement] cannot be combined with [XmlArrayItem].
  • The properties KeywordList and ResponseList are not public. XmlSerializer only serializes public members.

Sample fiddle.

Upvotes: 1

Haydn
Haydn

Reputation: 303

Found the answer. The properties can't be set because they are not public.

Upvotes: 0

Related Questions