Fischermaen
Fischermaen

Reputation: 12458

XmlSerializer, GenerateSerializer and Collections

We have some setup classes in our project, which are serialized / deserialized with XmlSerializer from some .config-Files. In some of those setup classes we have collections of sub-setups like this:

using System;
using System.Collections.ObjectModel;
using System.Xml.Serialization;

namespace Datev.Framework.Shared.Actions.Setup
{
    [Serializable]
    [XmlSerializerAssembly]
    [XmlRoot("setup")]
    public class SetupXml
    {
        public SetupXml()
        {
            SubSetups = new Collection<SubSetupXml>();
        }

        [XmlArray("subSetups")]
        [XmlArrayItem("subSetup")]
        public Collection<SubSetupXml> SubSetups { get; private set; }
    }

    [Serializable]
    public class SubSetupXml
    {
        [XmlElement("someValue")]
        public string SomeValue { get; set; }
    }
}

We are using the attribute [XmlSerializerAssembly] to have the best performance for reading and writing the setups. And here is my problem: We are using Collection to avoid the CA-Warning "Don't use arrays". When we make the setter of SubSetups public, we get the CA-Warning CA2227 "Don't make the setter of a collection public". If we make the setter of the property SubSetups private (like in the code sample), we'll get an error in the generated serializer. The method "GenerateSerializer" (invoked in a tool of us) the code has a line like this:

if (o.SubSetups == null) o.SubSetups = new Collection<SubSetupXml>();

If we make the setter private, we'll get a CS0200 "Property SubSetups cannont be assigned" during building the serializer. Does anyone know how to make a correct setup with a generated serializer without suppressing a CA-Warning?

Upvotes: 8

Views: 828

Answers (6)

Phil
Phil

Reputation: 42991

Changing the type of SubSetups to IEnumerable will get rid of the code analysis warning, but I can't tell if this is appropriate for you.

    [XmlArray("subSetups")]
    [XmlArrayItem("subSetup")]
    public IEnumerable<SubSetupXml> SubSetups { get; set; }

Upvotes: 3

Radin Gospodinov
Radin Gospodinov

Reputation: 2323

You can try to implement IXmlSerializable interface. It requires a bit more work but it gives you fine control on the serialization, and in your case access to the private class variables. In ReadXml method you just have to create and instance of your collection, iterate over each node in the source xml and parse the value

Upvotes: 3

JoeKir
JoeKir

Reputation: 1117

I assume that you have already consulted this article? http://msdn.microsoft.com/en-us/library/ms182327.aspx

This strikes me as an important note in that article: "Both binary and XML serialization support read-only properties that are collections. The System.Xml.Serialization.XmlSerializer class has specific requirements for types that implement ICollection and System.Collections.IEnumerable in order to be serializable."

Further to that you may be able to make more progress with the .Clear() and then .AddRange() approach, detailed there.

Upvotes: 3

Simon Mourier
Simon Mourier

Reputation: 138915

You could have something like this:

    public class SetupXml
    {
        public SetupXml()
        {
            SubSetups = new Collection<SubSetupXml>();
        }

        [XmlIgnore]
        public Collection<SubSetupXml> SubSetups { get; private set; }

        [EditorBrowsable(EditorBrowsableState.Never)]
        [GeneratedCodeAttribute("Whatever", "1.0.0.0")]
        [XmlArray("subSetups")]
        [XmlArrayItem("subSetup")]          
        public SubSetupXml[] SerializationSubSetups
        {
            get
            {
                return SubSetups.ToArray();
            }
            get
            {
                SubSetups = new SubSetups();
                if (value != null)
                {
                    foreach(SubSetupXml ssx in value)
                    {
                        SubSetups.Add(ssx);
                    }
                }
            }
        }
    }

It's not perfect, but the EditorBrowsable attribute will prevent developers using this library (from another assembly) to see it displayed by intellisense/autocompletion tools. And the GeneratedCode attribute will prevent CA warning on it.

Upvotes: 3

Grx70
Grx70

Reputation: 10349

If the situation is as straightforward as it seems then You don't need to check whether (o.SubSetups == null) because You have the line SubSetups = new Collection<SubSetupXml>(); in the SetupXml() constructor (that is, of course, if o is of type SetupXml). If you get rid of that if statement from the GenerateSerializer method and make the setter private You should be fine - there's no way the SubSetups property can be null unless there are some other ways of messing around with it that You didn't mention...

Upvotes: 3

Lorenzo Dematt&#233;
Lorenzo Dematt&#233;

Reputation: 7839

It is hard to tell: a "correct" setup depends highly on the context. Just a quick idea: what happens if you move the logic from "GenerateSerializer" to the property getter? Would it be acceptable?

[XmlArray("subSetups")]
[XmlArrayItem("subSetup")]
public Collection<SubSetupXml> SubSetups { 
   get {
      // subSetups needs to be a backing (private) field... is this a problem?
      if (this.subSetups == null) this.subSetups = new Collection<SubSetupXml>();
   }
   private set; 
}

This way, in "GenerateSerializer" you just get the collection. If the collection has not already been created, it will be inside the getter, without needing to create it outside the class. Just an idea, let me know if it is not applicable.

Upvotes: 5

Related Questions