3

I am trying to serailize an Object into XML. Below is the XML format which I need.

<parameters>
  <parameter>
    <key>Key1</key>
    <value>Value1</value>
    <key>Key2</key>
    <value>Value2</value>
    <key>Key3</key>
    <value>value3</value>
  </parameter>
</parameters>

Below is the Model I have created

[Serializable]
[XmlType("parameters")]
public class parameters
{
    public List<parameter<string,string>> parameter { get; set; }
}

[Serializable]
[XmlType("parameter")]
public class parameter<K,V>
{
    public K key { get; set; }
    public V value { get; set; }

    public parameter() { }

    public parameter(K key, V value)
    {
        this.key = key;
        this.value = value;
    }
}

When I try to serialize to XML I get the below format

<parameters>
  <parameter>
    <parameter>
      <key>Key1</key>
      <value>Value1</value>
    </parameter>
    <parameter>
      <key>Key2</key>
      <value>Value2</value>
    </parameter>
    <parameter>
      <key>Key3</key>
      <value>Value3</value>
    </parameter>
  </parameter>
</parameters>

Kindly help me to solve this.

Viki888
  • 2,686
  • 2
  • 13
  • 16
  • You don't need multiple levels of parameter(s). Changing XmlArray to XmlElement eliminates the extra layer of tags. – jdweng May 25 '15 at 20:37
  • An valid `xml` must contain a root tag to contain it's childs. – Amit Kumar Ghosh May 25 '15 at 17:07
  • This is completely irrelevant. The root tag is ``, which is present, and it is clear that the xml _header_ was simply left off to keep the question compact; the output doesn't show it either, and output systems _always_ add that stuff. The question was answered and accepted _three years ago_. This answer has no added value. – Nyerguds Dec 14 '18 at 13:00

2 Answers2

1

From the point of view of XmlSerializer, the <parameter> element of your XML isn't a list of key/value pair classes, because there's no nesting of each pair in some containing element. Instead it's a polymorphic list, where each entry can be an element of type <key> or of type <value>. So the easiest way to handle this may be to use the built-in functionality of the serializer for handling lists of polymorphic types as follows:

public abstract class ParameterKeyOrValue<T>
{
    [XmlText]
    public T Text { get; set; }
}

public sealed class ParameterKey<T> : ParameterKeyOrValue<T>
{
}

public sealed class ParameterValue<T> : ParameterKeyOrValue<T>
{
}

[Serializable]
[XmlType("parameters")]
public class parameters
{
    [XmlIgnore]
    public List<parameter<string, string>> parameter { get; set; }

    [XmlArray("parameter")]
    [XmlArrayItem("key", typeof(ParameterKey<string>))]
    [XmlArrayItem("value", typeof(ParameterValue<string>))]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public ParameterKeyOrValue<string>[] XmlParameters
    {
        get
        {
            if (parameter == null)
                return null;
            return parameter.SelectMany(p => new ParameterKeyOrValue<string>[] { new ParameterKey<string> { Text = p.key }, new ParameterValue<string> { Text = p.value } }).ToArray();
        }
        set
        {
            if (value == null)
                parameter = null;
            else
            {
                if (value.Length % 2 != 0)
                    throw new ArgumentException("Unequal number of keys and values");
                var newParameters = value.OfType<ParameterKey<string>>().Zip(value.OfType<ParameterValue<string>>(), (k, v) => new parameter<string, string>(k.Text, v.Text)).ToList();
                // Make sure we got an equal number of keys and values.
                if (newParameters.Count != value.Length / 2)
                    throw new ArgumentException("Unequal number of keys and values");
                parameter = newParameters;
            }
        }
    }
}

The [XmlArrayItem("someTypeName", typeof(SomeType))] decorations tell the serializer that array elements of type SomeType (in this case ParameterKey<string> and ParameterValue<string>) are to be serialized with element name "someTypeName" ("key" and "value", respectively.)

dbc
  • 104,963
  • 20
  • 228
  • 340
0

You either need to make Parameter contain a list of the key values (by the way .net has several collections that do this for you such as Dictionary) e.g:

public class Parameter<K,V>{
  public Dictionary<K,V> Values {get; set;}

  public Parameter()
  {
     Values = new Dictionary<K,V>();
  }

  public void AddParameter(K key, V value)
  {
     Values.Add(key,value);
  }

  ...Other access methods here
}

Or in parameters just make it a list of key values or a dictionary:

public class Parameters<K,V>{

   public Dictionary<K,V> Parameters {get; set;}

   public Parameters(){
       Parameters = new Dictionary<K,V>();
   }
}

The second option is obviously the best one as the first is just re-inventing the .net Dictionary. There doesn't seem to be any benefit to the first.

This is bread and butter stuff so if you don't really understand arrays and .net collections you should familiarize yourself with this area. Searching on stack overflow will give you all you need.

You can use XmlElementAttribute.ElementName property to name the Parameters Dictionary as you like and it should produce the results you want although I would question why inside the parameters node you only want one parameter node?

Luthervd
  • 1,388
  • 2
  • 14
  • 25