Bumper
Bumper

Reputation: 387

C# deserialize xml array of different types into multiple arrays

I have the following xml:

<Applications>
    <AccessibleApplication></AccessibleApplication>
    <AccessibleApplication></AccessibleApplication>
    <EligibleApplication></EligibleApplication>
    <EligibleApplication></EligibleApplication>
</Applications>

Is there a way to deserialize this into a C# object so that the AccessibleApplications and EligibleApplications are two separate arrays? I tried the following but get an exception because "Applications" is used more than once.

[XmlArray("Applications")]
[XmlArrayItem("AccessibleApplication")]
public List<Application> AccessibleApplications { get; set; }

[XmlArray("Applications")]
[XmlArrayItem("EligibleApplication")]
public List<Application> EligibleApplications { get; set; }

The exception is: The XML element 'Applications' from namespace '' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the element.

Is this possible to do?

Thanks!

Edit: I forgot to mention that I do not want to have an "Applications" class just for the sake of providing a container object for the two arrays. I have several situations like this and I don't want the clutter of these classes whose only purpose is to split up two arrays of the same type.

I was hoping to be able to deserialize the two arrays into an outer object using some sort of tag like [XmlArrayItem="Application/AccessibleApplication"] without creating an "Applications" class.

Upvotes: 6

Views: 2400

Answers (3)

Eulogy
Eulogy

Reputation: 317

You can use this class to create objects from strings, creating strings from objects and create byte [] from objects

StringToObject

var applicationObject = new XmlSerializerHelper<Applications>().StringToObject(xmlString);

ObjectToString

var xmlString = new XmlSerializerHelper<Applications>().ObjectToString(applicationObject);

ObjectToByteArray

var byteArray = new XmlSerializerHelper<Applications>().ObjectToByteArray(applicationObject);

The XmlSerializerHelper:

namespace StackOverflow
{
    public class XmlSerializerHelper<T> where T : class
    {
        private readonly XmlSerializer _serializer;

        public XmlSerializerHelper()
        {
            _serializer = new XmlSerializer(typeof(T));

        }

        public T ToObject(string xml)
        {
            return (T)_serializer.Deserialize(new StringReader(xml));
        }

        public string ToString(T obj, string encoding)
        {
            using (var memoryStream = new MemoryStream())
            {
                _serializer.Serialize(memoryStream, obj);
                return Encoding.GetEncoding(encoding).GetString(memoryStream.ToArray());
            }
        }

        public byte[] ToByteArray(T obj, Encoding encoding = null)
        {
            var settings = GetSettings(encoding);
            using (var memoryStream = new MemoryStream())
            {
                using (var writer = XmlWriter.Create(memoryStream, settings))
                {
                    _serializer.Serialize(writer, obj);
                }
                return memoryStream.ToArray();
            }
        }

        private XmlWriterSettings GetSettings(Encoding encoding)
        {
            return new XmlWriterSettings
            {
                Encoding = encoding ?? Encoding.GetEncoding("ISO-8859-1"),
                Indent = true,
                IndentChars = "\t",
                NewLineChars = Environment.NewLine,
                ConformanceLevel = ConformanceLevel.Document
            };
        }
    }
 }

Your Class:

[XmlRoot]
public class Applications
{
    [XmlElement("AccessibleApplication")]
    public string[] AccessibleApplication { get; set; }
    [XmlElement("EligibleApplication")]
    public string[] EligibleApplication { get; set; }
}

Or

[XmlRoot]
public class Applications
{
    [XmlElement("AccessibleApplication")]
    public List<string> AccessibleApplication { get; set; }
    [XmlElement("EligibleApplication")]
    public List<string> EligibleApplication { get; set; }
}

Cheers.

Upvotes: 0

Volkan Paksoy
Volkan Paksoy

Reputation: 6977

You can use XmlElement attribute to deserialize to different lists:

public class Applications
{
    [XmlElement("AccessibleApplication")]
    public List<Application> AccessibleApplications { get; set; }

    [XmlElement("EligibleApplication")]
    public List<Application> EligibleApplications { get; set; }
}

public class Application
{
    [XmlText]
    public string Value { get; set; }
}

So for a sample XML:

<Applications>
    <AccessibleApplication>xyz</AccessibleApplication>
    <AccessibleApplication>abc</AccessibleApplication>
    <EligibleApplication>def</EligibleApplication>
    <EligibleApplication>zzz</EligibleApplication>
</Applications>

The following snippet would output the below:

using (var reader = new StreamReader("XMLFile1.xml"))
{
    var serializer = new XmlSerializer(typeof(Applications));
    var applications = (Applications)serializer.Deserialize(reader);

    Console.WriteLine("AccessibleApplications:");
    foreach (var app in applications.AccessibleApplications)
    {
        Console.WriteLine(app.Value);
    }

    Console.WriteLine();

    Console.WriteLine("EligibleApplications:");
    foreach (var app in applications.EligibleApplications)
    {
        Console.WriteLine(app.Value);
    }
}

Output:

AccessibleApplications:

xyz

abc

EligibleApplications:

def

zzz

Upvotes: 0

hevans900
hevans900

Reputation: 897

I've found a fairly neat way to do this, first make a class like this:

using System.Xml.Serialization;

[XmlRoot]
public class Applications
{
    [XmlElement]
    public string[] AccessibleApplication;
    [XmlElement]
    public string[] EligibleApplication;
}

Notice how the elements are individual arrays. Now using this class (I had my XML in a separate file hence the XmlDocument class).

var doc = new XmlDocument();
doc.Load("../../Apps.xml");
var serializer = new XmlSerializer(typeof(Applications));
Applications result;

using (TextReader reader = new StringReader(doc.InnerXml))
{
    result = (Applications)serializer.Deserialize(reader);
}

Now to prove this works you can write this all in to a console app and do a foreach to print all the values in your arrays, like so:

foreach (var app in result.AccessibleApplication)
{
    Console.WriteLine(app);
}
foreach (var app in result.EligibleApplication)
{
    Console.WriteLine(app);
}

Upvotes: 1

Related Questions