Frage

I want to create a serializable C# class that would serialize into:

<metadata>
    <entry key="">
        <dimensionInfo>
            <enabled>false</enabled>
        </dimensionInfo>
    </entry>
    <entry key="">false</entry>
</metadata>

Can you help me? I can't handle different entry node structure:/ Too hard for me:P

War es hilfreich?

Lösung

Let's begin from the lowest class in the hierarchy:

[Serializable]
public class DimensionInfo
{
    [XmlElement("enabled")]
    public Boolean Enabled { get; set; }
    public DimensionInfo()
    {
    }
}

As you see, there's nothing special here. Then, proceed with the next:

[Serializable]
public class Entry
{
    private DimensionInfo _dimensionInfo = default(DimensionInfo);
    private Boolean _containsDimensionInfo = true;

    [XmlAttribute("key")]
    public String Key { get; set; }

    [XmlText(typeof(String))]
    public String ContainsDimensionInfo
    {
        get
        {
            return CheckDimensionContaining().ToString().ToLower();
        }
        set
        {
            _containsDimensionInfo = Boolean.Parse(value);
        }
    }

    [XmlIgnore]
    public Boolean ContainsDimensionInfoSpecified
    {
        get { return !CheckDimensionContaining(); }
    }

    [XmlElement("dimensionInfo")]
    public DimensionInfo DimensionInfo
    {
        get { return _dimensionInfo; }
        set { _dimensionInfo = value; }
    }

    [XmlIgnore]
    public Boolean DimensionInfoSpecified
    {
        get { return CheckDimensionContaining(); }
    }

    public Entry()
    {
        Key = String.Empty;
        CheckDimensionContaining();
    }

    private Boolean CheckDimensionContaining()
    {
        return _containsDimensionInfo = _dimensionInfo != default(DimensionInfo);
    }
}

Here the magic begins. With using selective XmlIgnoreAttribute we can decide the ways the object serializes. The main class only wraps a list of Entries:

[Serializable]
[XmlRoot("metadata")]
public class Metadata
{
    [XmlElement("entry")]
    public List<Entry> Entries { get; set; }

    public Metadata()
    {
        Entries = new List<Entry>();
    }
}

How it is made: entry contains dimensionInfo if it exists, and false if it does not. Try out the code sample in console:

Metadata metadata = new Metadata();
metadata.Entries.Add(new Entry()); //Adding empty entry
//Adding entry with info
metadata.Entries.Add(new Entry() { DimensionInfo = new DimensionInfo() });
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Metadata));
using (FileStream fileStream = new FileStream("info.txt", FileMode.Create))
{
    xmlSerializer.Serialize(fileStream, metadata);
}

It gave me the following output:

<metadata>
  <entry key="">false</entry>
  <entry key="">
    <dimensionInfo>
      <enabled>false</enabled>
    </dimensionInfo>
  </entry>
</metadata>

However, this code is not perfect and there's a lot of things to improve, but now you have an idea of how it works

Andere Tipps

If you ever encounter XML files that you need to consume via .Net there is a tool XML Schema Definition Tool (Xsd.exe) that does the conversion automatically.

Its a command-line tool and to open it you use the Start > All Programs > Visual Studio 2008/10/12/13 > Visual Studio Tools > Visual Studio Command Prompt

enter image description here

The syntax to create a class from the XML file is described in the MSDN article. I'll give you an overview of the process to get you started.

  • Save the xml file to the temp dirtectory:
<metadata>
    <entry key="">
        <dimensionInfo>
            <enabled>false</enabled>
        </dimensionInfo>
    </entry>
    <entry key="">false</entry>
</metadata>
  • Fire up the Visual Studio Command Prompt
  • To find out all the options enter xsd /?
  • Now you need to convert the XML file to a XSD file, this is the command:

xsd "C:\temp\File.xml" /c /outputdir:c:\temp

This will create an XSD file in the temp directory.

  • Then to convert the XSD file to a Serializable C# class this is the command:

xsd "C:\temp\File.xsd" /c /outputdir:c:\temp

This is the resulting C# Serialized class file:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)]
public partial class metadata {

    private metadataEntry[] itemsField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("entry", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public metadataEntry[] Items {
        get {
            return this.itemsField;
        }
        set {
            this.itemsField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
public partial class metadataEntry {

    private metadataEntryDimensionInfo[] dimensionInfoField;

    private string keyField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("dimensionInfo", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public metadataEntryDimensionInfo[] dimensionInfo {
        get {
            return this.dimensionInfoField;
        }
        set {
            this.dimensionInfoField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string key {
        get {
            return this.keyField;
        }
        set {
            this.keyField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
public partial class metadataEntryDimensionInfo {

    private string enabledField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string enabled {
        get {
            return this.enabledField;
        }
        set {
            this.enabledField = value;
        }
    }
}

Sounds like;

public class Metadata
{ 
    private Dictionary<string, object> entry;
}

Where the dictionary has two item; "false" and another object;

public class dimensionInfo
{
    public bool enabled = false;
}

However, you have 2 keys that are empty string. Are you sure the keys shouldn't have a value?

Actually you can create your own custom serialization for any class, so you don't have to stick to the schema when implementing your class.

All you have to do is to create a class which implements the IXmlSerializable interface

You can read more on the topic here: Custom XML serialization

Otherwise you can use Attributes to control XML serialization.

Using [XmlAttribute] results in serialization as attribute.

Using [XmlText] results in serialization as inner text in the node. (Like the false string inside the other element in your example XML.)

Example:

public class Program
{
    static void Main(string[] args)
    {
        Metadata meta = new Metadata();
        meta.entry = new List<Entry>();
        var dim = new dimensionInfo();
        meta.entry.Add(
            new Entry()
            {
                key = "",
                O = dim
            }
            );
        meta.entry.Add(
            new Entry()
            {
                key = "",
                text = "false",
                O = null
            }
            );


        XmlWriterSettings set = new XmlWriterSettings();
        set.NamespaceHandling = NamespaceHandling.OmitDuplicates;
        set.OmitXmlDeclaration = true;
        set.DoNotEscapeUriAttributes = false;
        set.Indent = true;
        set.NewLineChars = "\n\r";
        set.IndentChars = "\t";

        XmlWriter writer = XmlWriter.Create(Console.Out, set);
        XmlSerializer ser = new XmlSerializer(typeof(Metadata), "");
        XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
        namespaces.Add(string.Empty, string.Empty);


        ser.Serialize(writer, meta, namespaces);
    }

    [XmlRoot("metadata")]
    public class Metadata
    {
        [XmlElement]
        public List<Entry> entry;
    }

    public class dimensionInfo
    {
        [XmlElement]
        public bool enabled = false;
    }


    public class Entry
    {
        [XmlAttribute] // serialized as attribute
        public string key = "";

        [XmlText] // serialized as text node
        public string text = "";

        [XmlElement("dimensionInfo")] // serialized as an element
        public dimensionInfo O = null;
    }
}

This resulted the following XML:

<metadata>
    <entry key=""><dimensionInfo><enabled>false</enabled></dimensionInfo></entry>
    <entry key="">false</entry>
</metadata>

You can use XSD.exe to autoGenerate C# class in which you can serialize.

A straight forward answer:

Having your class to implement IXmlSerializable interface and using an XmlSerializer to defining the behavior for all the cases you desire.

private DimensionInfo _value;

public void WriteXml(XmlWriter writer)
{
    var valueSerializer = new XmlSerializer(typeof (DimensionInfo));

    var ns = new XmlSerializerNamespaces();
    ns.Add("", "");

    writer.WriteStartElement("entry");
    writer.WriteAttributeString("key", string.Empty, string.Empty);

    // Here you define how you want your XML structure to look like
    // E.g. write an empty XML node in case of a null value
    if (_value != null) 
    {
        valueSerializer.Serialize(writer, value, ns);
    }
    writer.WriteEndElement();
}

Would produce this in XML

  <entry key="">
    <dimensionInfo>
      <enabled>true</enabled>
    </dimensionInfo>
  </entry>

Or in case of a null value

  <entry key="" />

A more detailed example with a XmlSerializableDictionary:

I used an XmlSerializableDictionary approach to produce the XML you provided. Again, you can specify precisely how the produced XML should look like in the WriteXml method.

[XmlRoot("metadata")]
public class XmlSerializableDictionary<TValue> : Dictionary<string, TValue>, IXmlSerializable where TValue : class
{
    private const string XmlKeyName = "key";
    private const string XmlValueName = "entry";

    public void WriteXml(XmlWriter writer)
    {
        var valueSerializer = new XmlSerializer(typeof (TValue));

        var ns = new XmlSerializerNamespaces(); ns.Add("", "");

        foreach (var key in Keys)
        {
            writer.WriteStartElement(XmlValueName);
            writer.WriteAttributeString(XmlKeyName, string.Empty, key);
            var value = this[key];

            // Only serialize the value if value is not null, otherwise write the 
            // empty XML element.
            if (value != null) 
            {
                valueSerializer.Serialize(writer, value, ns);
            }
            writer.WriteEndElement();
        }
    }

    public void ReadXml(XmlReader reader) { /* left out */ }
    public XmlSchema GetSchema() { return null; }
}

The DimensionInfo type for reference

[XmlRoot("dimensionInfo")]
public class DimensionInfo
{
    [XmlElement("enabled")]
    public Boolean Enabled { get; set; }
}

The following code serializes the dictionary to XML

var xmlSerializableDictionary = new XmlSerializableDictionary<DimensionInfo>
{
    {"justakey", new DimensionInfo {Enabled = true}},
    {"anotherkey", null}
};

var xmlSerializer = new XmlSerializer(typeof (SerializableDictionary<DimensionInfo>));
xmlSerializer.Serialize(File.Open(@"D:\xmlSerializedDictionary.xml", FileMode.Create), serializableDictionary);

Produced XML file:

<?xml version="1.0"?>
<metadata>
  <entry key="justakey">
    <dimensionInfo>
      <enabled>true</enabled>
    </dimensionInfo>
  </entry>
  <entry key="anotherkey" />
</metadata>
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top