views:

22

answers:

2

I'm writing in C# and trying to represent an XML config file through an IXmlSerializable class. I'm unsure how to represent the nested elements in my config file, though, such as logLevel:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <logging>
    <logLevel>Error</logLevel>
  </logging>
  <credentials>
    <user>user123</user>
    <host>localhost</host>
    <password>pass123</password>
  </credentials>
  <credentials>
    <user>user456</user>
    <host>my.other.domain.com</host>
    <password>pass456</password>
  </credentials>
</configuration>

There is an enum called LogLevel that represents all the possible values for the logLevel tag. The tags within credentials should all come out as strings. In my class, called DLLConfigFile, I had the following:

[XmlElement(ElementName="logLevel", DataType="LogLevel")]
public LogLevel LogLevel;

However, this isn't going to work because <logLevel> isn't within the root node of the XML file, it's one node deeper in <logging>. How do I go about doing this?

As for the <credentials> nodes, my guess is I will need a second class, say CredentialsSection, and have a property such as the following:

[XmlElement(ElementName="credentials", DataType="CredentialsSection")]
public CredentialsSection[] AllCredentials;

Edit: okay, I tried Robert Love's suggestion and created a LoggingSection class. However, my test fails:

var xs = new XmlSerializer(typeof(DLLConfigFile));

using (var stream = new FileStream(_configPath, FileMode.Open,
    FileAccess.Read, FileShare.Read))
{
    using (var streamReader = new StreamReader(stream))
    {
        XmlReader reader = new XmlTextReader(streamReader);
        var file = (DLLConfigFile)xs.Deserialize(reader);
        Assert.IsNotNull(file);
        LoggingSection logging = file.Logging;
        Assert.IsNotNull(logging); // fails here
        LogLevel logLevel = logging.LogLevel;
        Assert.IsNotNull(logLevel);
        Assert.AreEqual(EXPECTED_LOG_LEVEL, logLevel);
    }
}

The config file I'm testing with definitely has <logging>. Here's what the classes look like:

[Serializable]
[XmlRoot("logging")]
public class LoggingSection : IXmlSerializable
{
    public XmlSchema GetSchema()
    {
        return null;
    }

    [XmlElement(ElementName="logLevel", DataType="LogLevel")]
    public LogLevel LogLevel;

    public void ReadXml(XmlReader reader)
    {
        LogLevel = (LogLevel)Enum.Parse(typeof(LogLevel),
            reader.ReadString());
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteString(Enum.GetName(typeof(LogLevel), LogLevel));
    }
}

[Serializable]
[XmlRoot("configuration")]
public class DLLConfigFile : IXmlSerializable
{
    [XmlElement(ElementName="logging", DataType="LoggingSection")]
    public LoggingSection Logging;
}
+1  A: 

Create a subclass that has a single property LogLevel and then create a logging property on your base class that is of the the new type.

Robert Love
I tried something like this. Should the new `LoggingSection` class implement `IXmlSerializable`, or can it just define `XmlRoot`, etc. attributes as necessary?
Sarah Vessels
This worked. I had to define `ReadXml` and `WriteXml` to do something specific for the log level in `LoggingSection`, and then make `ReadXml` and `WriteXml` in `DLLConfigFile` make use of `LoggingSection`'s `ReadXml` and `WriteXml`.
Sarah Vessels
A: 

Take a look at OXM. I think it's much easier to use to serialize to XML.

Delucia