0

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.

dbc
  • 104,963
  • 20
  • 228
  • 340
Stuie_M
  • 122
  • 11
  • 1
    Could you please [edit] your question to share a [mcve]? As it is your code doesn't compile because it is missing a definition for `IUniqueObjectIdentifier`. I made some guesses here: https://dotnetfiddle.net/YBHxvA but we still need a complete example to help. – dbc Nov 21 '18 at 01:04
  • 1
    Can't reproduce, see https://dotnetfiddle.net/4xLiar. With my guesses for `IUniqueObjectIdentifier` the `Fields` collection is serialized and deserialized successfully. – dbc Nov 21 '18 at 01:12
  • Thanks dbc, your guess was right about the interface: public interface IUniqueObjectIdentifier { Guid Id { get; set; } } – Stuie_M Nov 21 '18 at 07:43
  • Also, you've put the Field element in the xml as 'Fields' but missed out the parent 'Fields' element. Thanks for looking at this. Does that help? – Stuie_M Nov 21 '18 at 07:45
  • I've updated and this seems to work, but nit in my code :-( https://dotnetfiddle.net/ArFLmo – Stuie_M Nov 21 '18 at 07:55

1 Answers1

0

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.

Stuie_M
  • 122
  • 11