c# XML de-serialisation

69 Views Asked by At

Pulling out my hair again. I've spent all day looking at this and can't figure out if its possible or not, or what i'm doing wrong.

I've created a custom object collection.

 public class ObjectCollection<T> : IEnumerable<T>, IEnumerable where T : IUniqueObjectIdentifier
{

    protected List<T> List = new List<T>();
    protected Dictionary<Guid, int> Keys = new Dictionary<Guid, int>();
    protected Dictionary<int, Guid> Inverse = new Dictionary<int, Guid>();
    protected Dictionary<string, Guid> Name = new Dictionary<string, Guid>();
    public void Add(T item)
    {
        if (item is IUniqueObjectIdentifier itemIdentifier)
            if (!Keys.ContainsKey(itemIdentifier.Id))
            {
                List.Add(item);
                Keys.Add(itemIdentifier.Id, List.IndexOf(item));
                Inverse.Add(List.IndexOf(item), itemIdentifier.Id);
            }
    }
    public void Remove(T item)
    {
        if (item is IUniqueObjectIdentifier itemIdentifier)
            if (List.Contains(item))
            {
                int index = List.IndexOf(item);
                Guid key = Inverse[index];
                Keys.Remove(itemIdentifier.Id);
                Inverse.Remove(index);
                List.Remove(item);
            }
    }
    public void Remove(int index)
    {
        if (index < List.Count)
        {
            Guid key = Inverse[index];
            Keys.Remove(key);
            List.RemoveAt(index);
            Inverse.Remove(index);
        }
    }

    public void Remove(Guid key)
    {
        if (Keys.ContainsKey(key))
        {
            int index = Keys[key];
            Keys.Remove(key);
            List.RemoveAt(index);
            Inverse.Remove(index);
        }
    }

    public void Clear()
    {
        Keys.Clear();
        List.Clear();
        Inverse.Clear();
    }

    public int Count => List.Count;

    public bool Contains(T item)
    {
        return List.Contains(item);
    }

    public bool ContainsKey(Guid key)
    {
        return Keys.ContainsKey(key);
    }

    public T this[int index]
    {
        get
        {
            if (index < List.Count)
                return List[index];
            else
                return default(T);
        }
    }

    public T this[Guid key]
    {
        get
        {
            if (Keys.ContainsKey(key))
                return List[Keys[key]];
            else
                return default(T);
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return List.GetEnumerator();
    }

    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    { return List.GetEnumerator(); }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return List.GetEnumerator();
    }

I then have have a Thing:

public class Thing
{
    //Collections
    [XmlElement(ElementName = "Fields")]
    public Fields Fields { get; set; }

The Fields object inherits from ObjectCollection:

[XmlRoot(ElementName = "Field")]
public class Fields:ObjectCollection<Field>
{

}

Finally I have a field class which contains a whole host of properties:

[XmlRoot(ElementName = "Field")]
public class Field : IUniqueObjectIdentifier
{
    [XmlElement(ElementName = "FLAGS")]
    public int Flags{ get; set; }

The ONLY way this seems to work is if I create my fields class like this:

[XmlRoot(ElementName = "Fields")]
public class Fields
{
    [XmlElement(ElementName = "Field")] private List<Field> Items { get; set; }
}

Which I hate. Having to access it, e.g. Thing.Fields.Items.Add rather than Thing.Fields.Add. makes no sense to me.

The XML is like this:

<Thing ID="BD825D4AD7F44C00B41E8827646EE196" Name="Thingy">
  <Fields>
    <Field ID="02A5DA70FD94495E963DA5D7E414E30B" NAME="Fieldy">
      <FLAGS>1</FLAGS>
    </Field>
    <Field ID="DAF609FFD05B413F9F9D0DA8DD241CB3" NAME="Fieldy2">
      <FLAGS>1</FLAGS>

When I try to de-serialise it I just get an Default field, i.e. it adds to the objectcollection but only one items, and its obviously failed to de-serialise the field as its essentially empty.

I've tried to see if I can figure out what attributes might help but I'm not sure. I also looked at creating custom de-serialisation, but this seems overkill.

Can anyone help point me in the right direction. I just want a neat and tidy solution - with the least amount of complexity!!! Like we all do :-)

Any help would be much appreciated, before I jump off the local pier.

Cheers,

Stu.

1

There are 1 best solutions below

0
On

Ok, I simplified the example a bit in my post from:

<Thing ID="BD825D4AD7F44C00B41E8827646EE196" Name="Thingy">
  <Fields>
    <AField ID="02A5DA70FD94495E963DA5D7E414E30B" NAME="Fieldy">
      <FLAGS>1</FLAGS>
    </AField>
    <AField ID="DAF609FFD05B413F9F9D0DA8DD241CB3" NAME="Fieldy2">
      <FLAGS>1</FLAGS>

to:

<Thing ID="BD825D4AD7F44C00B41E8827646EE196" Name="Thingy">
  <Fields>
    <Field ID="02A5DA70FD94495E963DA5D7E414E30B" NAME="Fieldy">
      <FLAGS>1</FLAGS>
    </Field>
    <Field ID="DAF609FFD05B413F9F9D0DA8DD241CB3" NAME="Fieldy2">
      <FLAGS>1</FLAGS>

AND:

[XmlRoot(ElementName = "AField")]
public class Fields:ObjectCollection<Field>
{

}

To:

[XmlRoot(ElementName = "Field")]
public class Fields:ObjectCollection<Field>
{

}

The answer is that EVEN THOUGH you specify the root, it will not automatically map correctly to the object; YOU NEED TO SPECIFY IT [XmlType("AField")], obviously unless they are the same. In the posted case: XmlRoot(ElementName = "Field")] = <Field ID="02A5DA70FD94495E963DA5D7E414E30B" NAME="Fieldy">

So this works:

[XmlRoot(ElementName = "AField")]
[XmlType("AField")]
public class Fields:ObjectCollection<Field>
{

}

Lessons learnt:

  1. Don't simply your examples; post them AS IS!
  2. When you've got a problem break it down to the smallest actions to over come it.

I can't help feel that's a bit backwards given the fact that you've mapped it with the root element but hey. Thanks to dbc in their help with this or I feel I would have been at the bottom of the sea. Up-voted for sheer effort.

Phew!

Stu.