0

I have a little problem that's been bugging my head for a few days. I am writing a kind of a manger tool for a virtual racing league. I want to save all my data into a single xml-file so that it can easily be accessed from several parties. However, because of the limitation of the xml-serializer, id have to make all my classes public. For that i have written an interface through which i want to convert my original managing objects (with all the functionality) into raw data containing objects for xml-serialization.

To be able to track who has created/changed something every relevant needs to hold a "ChangeNotification" field. For that i created a base class with these fields and from which my container classes are derived.

And now here comes the problem: If I serialize only a part of it it all works fine. But as soon as i serialize an Object which contains another Object derived from the same base, my "CreateChangeNotification" will only be present in the top Object.

I don't see why it's doing that as there are clearly two instances of it saved in the Objects, holding different values at the same time.

Sorry for the long code snippet, but that is what i needed to recreate the problem and it seems to happen everytime with this structure.

[Serializable()]
public class DataManagerXmlContainer<DataContainerType> : IDataManagerContainer
{
    [XmlElement("Created")]
    public ChangeNotificationXmlContainer created;

    [XmlIgnore()]
    public IChangeNotificationDataContainer Created { get => created; set => created = new ChangeNotificationXmlContainer(value); }

    //[XmlElement("Changed")]
    //public ChangeNotificationXmlContainer changed;

    //[XmlIgnore()]
    //public IChangeNotificationDataContainer LastChange { get => changed; set => changed = new ChangeNotificationXmlContainer(value); }

    public DataManagerXmlContainer() { }
}

[Serializable()]
public class ReviewDataXmlContainer : DataManagerXmlContainer<IReviewDataContainer>, IReviewDataContainer
{
    [XmlArray("CommentList")]
    [XmlArrayItem("Comment")]
    public List<ReviewCommentXmlContainer> comments = new List<ReviewCommentXmlContainer>();
    [XmlIgnore()]
    public ICollection<IReviewCommentDataContainer> Comments
    {
        get => comments.Cast<IReviewCommentDataContainer>().ToList();
        set => comments = value.ToList().ConvertAll(x => new ReviewCommentXmlContainer(x));
    }

    public ReviewDataXmlContainer() { }

    public ReviewDataXmlContainer(string authorID)
    {
        created = new ChangeNotificationXmlContainer(authorID, DateTime.Now);
    }

    public void AddComment(IReviewCommentDataContainer comment)
    {
        comments.Add(new ReviewCommentXmlContainer(comment));
    }
}

[Serializable()]
public class ReviewCommentXmlContainer : DataManagerXmlContainer<IReviewCommentDataContainer>, IReviewCommentDataContainer
{
    [XmlAttribute("author_id")]
    public string AuthorAdminID { get; set; }
    public string Text { get; set; }

    public ReviewCommentXmlContainer() { }

    public ReviewCommentXmlContainer(string authorID, string text)
    {
        created = new ChangeNotificationXmlContainer(authorID, DateTime.Now);
        AuthorAdminID = authorID;
        Text = text;
    }

    public ReviewCommentXmlContainer(IReviewCommentDataContainer dataContainer)
    {
        AuthorAdminID = dataContainer.AuthorAdminID;
        Text = dataContainer.Text;
    }
}

[Serializable()]
public class ChangeNotificationXmlContainer : IChangeNotificationDataContainer
{
    [XmlAttribute("author_id")]
    public string AuthorID { get; set; }

    [XmlAttribute("time")]
    public DateTime Time { get; set; }

    public ChangeNotificationXmlContainer() { }

    public ChangeNotificationXmlContainer(string id, DateTime time)
    {
        AuthorID = id;
        Time = time;
    }

    public ChangeNotificationXmlContainer(IChangeNotificationDataContainer dataContainer)
    {
        AuthorID = dataContainer.AuthorID;
        Time = dataContainer.Time;
    }
}

public interface IChangeNotificationDataContainer
{
    string AuthorID { get; set; }
    DateTime Time { get; set; }
}

public interface IDataManagerContainer
{
    IChangeNotificationDataContainer Created { get; set; }
}

public interface IReviewDataContainer : IDataManagerContainer
{
    ICollection<IReviewCommentDataContainer> Comments { get; set; }
    void AddComment(IReviewCommentDataContainer comment);
}

public interface IReviewCommentDataContainer : IDataManagerContainer
{
    string AuthorAdminID { get; set; }
    string Text { get; set; }
}

And the Program code:

class Program
{
    static void Main(string[] args)
    {
        IReviewCommentDataContainer comment = new ReviewCommentXmlContainer("12345", "This is a test");
        Task.Delay(1000).Wait();
        ReviewDataXmlContainer review = new ReviewDataXmlContainer("23456");
        review.AddComment(comment);


        XmlSerializer serializer = new XmlSerializer(typeof(ReviewDataXmlContainer));
        TextWriter writer = new StreamWriter("Test.xml");
        serializer.Serialize(writer, review);

        //Task.Delay(-1).Wait();
    }
}

This is how my xml looks like:

<?xml version="1.0" encoding="utf-8"?>
<ReviewDataXmlContainer xmlns:xsi="http://www.w3.org/2001/XMLSchema instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Created author_id="23456" time="2018-11-16T06:56:16.5425819+01:00" />
    <CommentList>
        <Comment author_id="12345">
            <Text>This is a test</Text>
        </Comment>
    </CommentList>
</ReviewDataXmlContainer>

As you can see the Create element of Comment is just missing.

S Schulze
  • 127
  • 9
  • 1
    this is not actually answering your question but i have to ask - have you considered other options for persistence? if you like file-based why not https://www.sqlite.org/onefile.html. you may end up keeping your hair... – jazb Nov 16 '18 at 06:24
  • Thx. I will take a look into it. However, the main goal of the project is to learn how to work with interfaces and serialization and such. – S Schulze Nov 16 '18 at 06:36
  • When serializing a class that inherits another class you need an include : [XmlInclude(type)] – jdweng Nov 16 '18 at 10:11
  • Thx i will give that a go later. – S Schulze Nov 16 '18 at 12:12
  • @jdweng: upon researching [xmlinclude] i only came across usage for serializing derived types. In this case i want it to correctly serialize members of the base type, which it should already know. How would i use [xmlinclude] in this context? – S Schulze Nov 16 '18 at 13:03
  • See OPs posted code and following link : https://stackoverflow.com/questions/18570657/c-sharp-xml-serialization-of-derived-classes – jdweng Nov 16 '18 at 14:43

1 Answers1

0

I found the problem. When adding the comment object through the interface the changenotification is not taken with and gets lost. In my complete code the problem was a bit more masked becase i used reflection to automatically set all properties. However, i was only looking at the main interface-type and not all implemented ones. I changed the code and now it works fine. Thanks for the support though.

S Schulze
  • 127
  • 9