简体   繁体   English

序列化自定义配置

[英]Serializing custom configuration

I need some help on implementing a way to serialize a custom configuration.我需要一些帮助来实现一种序列化自定义配置的方法。 I started with the content of following example: Polymorphic custom configuration section我从以下示例的内容开始: 多态自定义配置部分

Reading the configuration works fine as expected but I can't save modifications of some configuration properties (eg changing property P1 to contain another string).读取配置按预期工作正常,但我无法保存对某些配置属性的修改(例如,将属性 P1 更改为包含另一个字符串)。 While debugging the content of the different objects looks fine (section contains collection which contains three proxy items which itself contain an instance of Parent class).虽然调试不同对象的内容看起来很好(部分包含包含三个代理项的集合,这些代理项本身包含一个父类的实例)。 The item which has been changed (P1="") has the isModified flag set to true (as expected).已更改的项目 (P1="") 的 isModified 标志设置为 true(如预期)。

When calling config.Save() some strange behaviour comes up and after three days of investigation (even the Microsoft base classes) I can't manage to find out where the problem is.当调用 config.Save() 时出现了一些奇怪的行为,经过三天的调查(甚至是 Microsoft 基类),我无法找出问题所在。 Here are some of my conclusions:以下是我的一些结论:

I added an override for each of the SerializeX methods (SerializeSection, SerializeElement and SerializeToXmlElement) and debbuged step by step through the code.我为每个 SerializeX 方法(SerializeSection、SerializeElement 和 SerializeToXmlElement)添加了一个覆盖,并通过代码逐步调试。

  • SerializeSection is called (as expected) with parameter parentElement which is not the section I want to serialize as the Collection property is empty (I would expect it to have the three instances which are part of the configuration file). SerializeSection使用参数 parentElement 调用(如预期),这不是我想要序列化的部分,因为 Collection 属性为空(我希望它具有作为配置文件一部分的三个实例)。 Calling base.SerializeSection with this instead of parentElement resolves the problem使用this而不是parentElement调用 base.SerializeSection 可以解决问题

  • SerializeToXmlElement is called before SerializeElement and does contain an instance of XmlWriter (as expected) SerializeToXmlElementSerializeElement之前调用并且确实包含 XmlWriter 的实例(如预期)

  • SerializeElement is called right after SerializeToXmlElement and does not contain the instance of XmlWriter anymore SerializeElementSerializeToXmlElement之后立即调用并且不再包含 XmlWriter 的实例

  • When entering the serialize methods of the collection object I would expect that the three elements of the collections are serialized.当进入集合对象的序列化方法时,我希望集合的三个元素被序列化。 But instead of the three items the collection only contains one item which has been freshly initialized and thus has a Parent property returning null.但该集合只包含一个新初始化的项目而不是三个项目,因此具有返回 null 的Parent属性。

I know that there needs to be a custom SerializeElement method (propably on the Proxy class) which then calls _Parent.ProxySerializeElement(writer, serializeCollectionKey) for each element as it does for deserializing.我知道需要有一个自定义的 SerializeElement 方法(可能在 Proxy 类上),然后像反序列化一样为每个元素调用_Parent.ProxySerializeElement(writer, serializeCollectionKey) But I can't manage to get it work.但我无法让它发挥作用。 An override of SerializeElement doesn't work as the XmlWriter instance is always null (even though the Proxy class has some IsModified method to check if the Parent object has changed). SerializeElement 的覆盖不起作用,因为XmlWriter 实例始终为 null (即使 Proxy 类具有一些 IsModified 方法来检查 Parent 对象是否已更改)。 In addition the Parent object is always null too, as soon as I enter this custom SerializeElement method.此外,一旦我输入此自定义 SerializeElement 方法,Parent 对象也始终为 null。

Here are the code fragments I added to the example:以下是我添加到示例中的代码片段:

Parent.cs父.cs

new public bool IsModified { get { return IsModified(); } }

public virtual bool ProxySerializeElement(XmlWriter writer, bool serializeCollectionKey)
{
    return SerializeElement(writer, serializeCollectionKey);
}

Proxy.cs代理.cs

protected override bool IsModified()
{
    bool isModified = base.IsModified();
    return isModified || (Parent == null ? false : Parent.IsModified);
}

protected override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey)
{
    bool serialize = base.SerializeElement(writer, serializeCollectionKey);
    return serialize || (_Parent == null ? false : _Parent.ProxySerializeElement(writer, serializeCollectionKey));
}

It's driving me nuts that I can't get it to work.它让我发疯,我无法让它工作。 Maybe someone else can help me out.也许其他人可以帮助我。

Thx in advance!提前谢谢!

Greetings, Stefi你好,史蒂菲

Finally this worked for me.最后这对我有用。 Maybe it helps someone else who's having the same problem.也许它可以帮助遇到同样问题的其他人。 I'll post the complete code to keep it simple.我将发布完整的代码以保持简单。 It might not be the firstclass solution but it's working.它可能不是一流的解决方案,但它正在起作用。 I would appreciate if someone else could have a look at it and propose a better way.如果其他人可以查看它并提出更好的方法,我将不胜感激。

What helped me to get it working is this article: https://www.codeproject.com/Articles/16466/Unraveling-the-Mysteries-of-NET-2-0-Configuration帮助我让它工作的是这篇文章: https : //www.codeproject.com/Articles/16466/Unraveling-the-Mysteries-of-NET-2-0-Configuration

My code was missing the elements collection (see ThingElement.cs).我的代码缺少元素集合(请参阅 ThingElement.cs)。

thing.config东西.config

<configuration>
  <configSections>
    <section name="ExampleSection" type="ConsoleApplication1.Things.ExampleSection, ConsoleApplication1" />
  </configSections>

  <ExampleSection>
    <things>
      <thing type="one" name="one-1" color="green" />
      <thing type="one" name="one-2" color="red" />
      <thing type="two" name="two-1" />
    </things>
  </ExampleSection>
</configuration>

ExampleSection.cs ExampleSection.cs

    public class ExampleSection : ConfigurationSection
    {
        static ExampleSection() { }

        [ConfigurationProperty("things")]
        [ConfigurationCollection(typeof(ThingElement), AddItemName = "thing",
            CollectionType = ConfigurationElementCollectionType.BasicMap)]
        public ExampleThingElementCollection Things
        {
            get { return (ExampleThingElementCollection)this["things"]; }
            set { this["things"] = value; }
        }
    }

ExampleThingElementCollection.cs ExampleThingElementCollection.cs

    [ConfigurationCollection(typeof(ThingElement), AddItemName = "thing",
       CollectionType = ConfigurationElementCollectionType.BasicMap)]
    public class ExampleThingElementCollection : ConfigurationElementCollection
    {
        #region Constructors
        public ExampleThingElementCollection()
        {
        }
        #endregion

        #region Properties
        public override ConfigurationElementCollectionType CollectionType
        {
            get { return ConfigurationElementCollectionType.BasicMap; }
        }
        protected override string ElementName
        {
            get { return "thing"; }
        }
        #endregion

        #region Indexers
        public ThingElement this[int index]
        {
            get { return (ThingElement)base.BaseGet(index); }
            set
            {
                if (base.BaseGet(index) != null)
                {
                    base.BaseRemoveAt(index);
                }
                base.BaseAdd(index, value);
            }
        }
        new public ThingElement this[string name]
        {
            get { return (ThingElement)base.BaseGet(name); }
        }
        #endregion

        #region Overrides
        protected override ConfigurationElement CreateNewElement()
        {
            return new ThingElement();
        }
        protected override object GetElementKey(ConfigurationElement element)
        {
            return (element as ThingElement).Name;
        }
        #endregion

        #region Methods
        public void Add(ThingElement thing)
        {
            base.BaseAdd(thing);
        }
        public void Remove(string name)
        {
            base.BaseRemove(name);
        }
        public void Remove(ThingElement thing)
        {
            base.BaseRemove(GetElementKey(thing));
        }
        public void Clear()
        {
            base.BaseClear();
        }
        public void RemoveAt(int index)
        {
            base.BaseRemoveAt(index);
        }
        public string GetKey(int index)
        {
            return (string)base.BaseGetKey(index);
        }
        #endregion
    }

ThingElement.cs (this class acts as proxy element) ThingElement.cs(此类充当代理元素)

    public class ThingElement : ConfigurationElement
    {
        #region Constructors
        /// <summary>
        /// Predefines the valid properties and prepares
        /// the property collection.
        /// </summary>
        static ThingElement()
        {
            // Predefine properties here
            s_propName = new ConfigurationProperty(
                "name",
                typeof(string),
                null,
                ConfigurationPropertyOptions.IsRequired
            );
        }
        #endregion

        #region Static Fields
        private static ConfigurationProperty s_propName;
        private static Dictionary<string, SpecialThing> elements = new Dictionary<string, SpecialThing>();
        #endregion


        #region Properties
        /// <summary>
        /// Gets the Name setting.
        /// </summary>
        [ConfigurationProperty("name", IsRequired = true)]
        public string Name
        {
            get { return (string)base[s_propName]; }
        }

        public SpecialThing Thing { get { return elements[Name]; } }

        protected override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey)
        {
            return Thing.ProxySerializeElement(writer, serializeCollectionKey);
        }

        protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
        {
            SpecialThing obj = null;
            string name = reader.GetAttribute("name");
            string type = reader.GetAttribute("type");
            switch (type)
            {
                case "one":
                    obj = new One();
                    break;
                case "two":
                    obj = new Two();
                    break;
                default:
                    throw new ArgumentException(string.Format("Invalid type: {0}", type));
            }

            base[s_propName] = name;
            if (!elements.ContainsKey(name))
                elements.Add(name, obj);
            obj.ProxyDeserializeElement(reader, serializeCollectionKey);
        }
        private Hashtable attributes;

        public Hashtable Attributes
        {
            get
            {
                if (attributes == null)
                    attributes = new Hashtable(StringComparer.OrdinalIgnoreCase);
                return attributes;
            }
        }

        protected override bool OnDeserializeUnrecognizedAttribute(String name, String value)
        {
            Attributes.Add(name, value);
            return true;
        }

        protected override void PreSerialize(XmlWriter writer)
        {
            if (attributes != null)
            {
                IDictionaryEnumerator e = attributes.GetEnumerator();
                while (e.MoveNext())
                {
                    string xmlValue = (string)e.Value;
                    string xmlName = (string)e.Key;

                    if ((xmlValue != null) && (writer != null))
                    {
                        writer.WriteAttributeString(xmlName, xmlValue);
                    }
                }
            }
        }

        #endregion
    }

SpecialThing.cs (parent class, eg base class if you have other which derive) SpecialThing.cs(父类,例如基类,如果您有其他派生类)

    public class SpecialThing : ConfigurationElement
    {
        [ConfigurationProperty("name", IsRequired = true)]
        public string Name
        {
            get
            {
                return (string)this["name"];
            }

            set
            {
                this["name"] = value;
            }
        }

        [ConfigurationProperty("type", IsRequired = true)]
        public string Type
        {
            get
            {
                return (string)this["type"];
            }

            set
            {
                this["type"] = value;
            }
        }

        public virtual bool ProxySerializeElement(XmlWriter writer, bool serializeCollectionKey)
        {
            return SerializeElement(writer, serializeCollectionKey);
        }

        public void ProxyDeserializeElement(XmlReader reader, bool serializeCollectionKey)
        {
            DeserializeElement(reader, serializeCollectionKey);
        }
    }

One.cs (parent class, eg base class if you have other which derive One.cs(父类,例如基类,如果您有其他派生

    public class One : SpecialThing
    {
        public One() { }

        public One(string name, string type, string color)
        {
            base.Name = name;
            base.Type = type;
            Color = color;
        }

        [ConfigurationProperty("color")]
        public string Color
        {
            get { return (string)this["color"]; }
            set { this["color"] = value; }
        }
    }

Two.cs二.cs

    public class Two : SpecialThing
    {
        public Two() { }

        public Two(string name, string type)
        {
            base.Name = name;
            base.Type = type;
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM