开发者

How do you Serialize just one of several Optional Elements in .Net?

I have a class with two parameters. One is the update time in Minutes and the other is the same value in seconds like this:

using System.Xml开发者_StackOverflow中文版.Serialization;

[XmlRoot(ElementName="config")]
public class Config
{
    [XmlElement(ElementName = "update_mins")]
    public int Minutes 
    {
        get
        {
            return this.Seconds / 60;
        }
        set
        {
            this.Seconds = value * 60;
        }
    }

    [XmlElement(ElementName = "update_secs")]
    public int Seconds { get; set; }
}

I get xml strings that can have either update_mins or update_secs but not both. I need to be able to use this class to deserialize these strings, this works ok. I also need to be able to searalize these classes and send them on, however when I do the serailzation it includes both update_secs and update_mins. Like this:

<config>
  <update_mins>23</update_mins>
  <update_secs>1380</update_secs>
</config>

When I want to get this:

<config>
  <update_secs>1380</update_secs>
</config>

I tried putting XmlIgnore on the update_mins value, but that stopped it deserializing the element. Is it possible to do this, and if so how?

My full code example is:

namespace Scrap
{
    class Program
    {
        static void Main(string[] args)
        {
            string s = "<config><update_mins>23</update_mins></config>";

            XmlSerializer searializer = new XmlSerializer(typeof(Config));
            Config result = (Config)searializer.Deserialize(new StringReader(s));

            Console.WriteLine("Minutes: " + result.Minutes);
            Console.WriteLine("Seconds: " + result.Seconds);

            Debug.Assert(result.Minutes == 23);
            Debug.Assert(result.Seconds == 1380);

            StringWriter s2w = new StringWriter();
            searializer.Serialize(s2w, result);

            Console.WriteLine(s2w.ToString());

            string xmlResult = "<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n" +
                "<config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n" +                
                "  <update_secs>1380</update_secs>\r\n" +
                "</config>";

            Debug.Assert(s2w.ToString() == xmlResult);

            Console.ReadKey(true);
        }
    }

    [XmlRoot(ElementName = "config")]
    public class Config
    {
        [XmlElement(ElementName = "update_mins")]        
        public int Minutes
        {            
            get
            {
                return this.Seconds / 60;
            }    
            set
            {
                this.Seconds = value * 60;
            }
        }

        [XmlElement(ElementName = "update_secs")]
        public int Seconds { get; set; }
    }
}


I would simply use [XmlIgnore] on Minutes since it is

  • Dependent on the Seconds
  • Calculated field
  • Redundant in data transfer

EDIT

You are mentioning that you do not have control over which element gets populated and it could be either or both.

One possible solution is to add one layer of abstraction.

  • Define internal public properties such as SecondsXml and MinutesXml and let them read XML values (Define attributes on them)
  • Keep Minutes and Seconds
  • have a boolean fields _isLoaded
  • Whenever Minutes or Seconds accessed, check _isLoaded and if false, then check values in SecondsXml and MinutesXml to figure out which one was loaded hence set Minutes and Seconds accordingly.

If you do not want to have SecondsXml and MinutesXml as public, define them as private but use DataContractSerializer for deserialization.


Try

class Config : IDeserializationCallback
{
    void IDeserializationCallback.OnDeserialization(object sender) 
    {
    }
}

or

class Config
{
    [OnDeserializing]
    void OnDeserialization(StreamingContext context) 
    {
    }
}


This is still not an ideal solution, but the way I have resolved this is to add [XmlIgnore] to the Minutes property (as Aliostad and others had suggested) and then add an AllElements property like this:

    [XmlAnyElement]
    public XmlElement[] AllElements
    {
        get
        {
            XmlElement[] value = new XmlElement[0];
            return value;
        }

        set
        {
            if (value != null)
            {
                foreach (XmlElement e in value)
                {
                    switch (e.Name)
                    {
                        case "update_mins":
                            this.Minutes = Convert.ToInt32(e.InnerText);
                            break;
                    }
                }
            }
        }
    }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜