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
andMinutesXml
and let them read XML values (Define attributes on them) - Keep
Minutes
andSeconds
- have a boolean fields
_isLoaded
- Whenever
Minutes
orSeconds
accessed, check_isLoaded
and if false, then check values inSecondsXml
andMinutesXml
to figure out which one was loaded hence setMinutes
andSeconds
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;
}
}
}
}
}
精彩评论