views:

177

answers:

7

The XmlSerializer does everything I want with one exception. I need to pair an element with another element as an attribute of that element. I don't want to write a completely custom serialize method. Here's my class:

public class Transaction { [XmlElement("ID")] public int m_id;

   [XmlElement("TransactionType")]
   public string m_transactiontype;

   [XmlAttribute("TransactionTypeCode")]
   public string m_transactiontypecode;

}

I instantiate and serialize as follows;

   Transaction tx = new Transaction();

   tx.m_id = 1;   
   tx.m_transactiontype = "Withdrawal";  
   tx.m_transactiontypecode = "520";

   StringWriter o = new
   StringWriter(CultureInfo.InvariantCulture);
   XmlSerializer s = new
   XmlSerializer(typeof(Transaction));   
   s.Serialize(o, tx);   
   Console.Write(o.ToString());

Gives me:

   <Transaction TransactionTypeCode="520">
     <ID>1</ID>
     <TransactionType>Withdrawal</TransactionType> 
   </Transaction>

I want:

   <Transaction>
     <ID>1</ID>
     <TransactionType TransactionTypeCode="520">Withdrawal</TransactionType>
   </Transaction>

Someone (Chris Dogget) suggested:

   public class Transaction
   {

       [XmlElement("ID")]
       public int m_id;

       public TransactionType m_transactiontype;
   }

   public class TransactionType
   {
       public TransactionType(){}
       public TransactionType(string type) { this.m_transactiontype = type; }

       [XmlTextAttribute]
       public string m_transactiontype;

       [XmlAttribute("TransactionTypeCode")]
       public string m_transactiontypecode; 
   }

The use of the TransactionType class looks promising - can you show me how you would instantiate the classes before serializing?

Thanks!

A: 

I guess you should create a separate class with Type and TypeCode fields. Decorate TypeCode with [XmlAttribute] and Type with [XmlText].

Dmitry Ornatsky
+1  A: 
public class Transaction
{

    [XmlElement("ID")]
    public int m_id;

    public TransactionType type;
}

public class TransactionType
{
    public TransactionType(){}
    public TransactionType(string type) { this.@Type = type; }

    [XmlTextAttribute]
    public string @Type;

    [XmlAttribute("TypeCode")]
    public string typecode; 
}

gives me this:

<?xml version="1.0" encoding="utf-16"?>
<Transaction xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
    <ID>1</ID>
    <type TypeCode="520">Withdrawal</type>
</Transaction>

Unfortunately, "Type" is an actual type in .NET, so you can't really use that (uppercase at least) as your field name.

Chris Doggett
> ""Type" is an actual type in .NET, so you can't really use that" -- Sure you can: it'll be in a different namespace, so things will work out okay. As for members that match other keywords you can just preface them with an @
Joel Coehoorn
+1  A: 

I believe the only way to do this is to create a class of Type (bad name though, given that it's a keyword in the framework as well ...) with member property TypeCode. Then you have a Type instance or reference as part of class Transaction.

andrewbadera
A: 

Implement IXmlSerializable and use Linq to XML to construct your serialized object.

This way you can control exactly how your xml looks.

Personally, I'd drop XmlSerialization, go with XAML and forget about trying to get my XML looks. It can look like crap, as long as it works!

Will
A: 

Your desired XML doesn't line up with the way you're laying out your objects in your code.

If you read the XML you are saying the following...

The transaction contains two elements: ID and Type. Type has an attribute called TypeCode

If you look at the object you're trying to create, you're saying this...

The transaction contains three properties: ID, Type, and TypeCode

...you can see the disconnect between the two. You have to align the goals of each to get proper serialization (modify either the class or the xml so the two align which what you're trying to communicate).

Justin Niessner
A: 

OK, let's remove the "'type' is a type in .NET" issue (my bad):

public class Transaction { [XmlElement("ID")] public int m_id;

   [XmlElement("TransactionType")]
   public string m_transactiontype;

   [XmlAttribute("TransactionTypeCode")]
   public string m_transactiontypecode;

}

I instantiate and serialize as follows;

Transaction tx = new Transaction();

tx.m_id = 1; tx.m_transactiontype = "Withdrawal"; tx.m_transactiontypecode = "520";

StringWriter o = new StringWriter(CultureInfo.InvariantCulture); XmlSerializer s = new XmlSerializer(typeof(Transaction)); s.Serialize(o, tx); Console.Write(o.ToString());

Gives me:

<Transaction TransactionTypeCode="520">
  <ID>1</ID>
  <TransactionType>Withdrawal</TransactionType>
</Transaction>

I want:

<Transaction>
  <ID>1</ID>
  <TransactionType TransactionTypeCode="520">Withdrawal</TransactionType>
</Transaction>

The use of the TransactionType class looks promising - can you show me how you would instantiate the classes before serializing?

Thanks!

GD screwed up my post - posting a new question...
+1  A: 

Assuming this code for your types:

public class Transaction
{
    public Transaction() { ttype = new TransactionType(); }

    [XmlElement("ID")]
    public int id;

    [XmlElement("TransactionType")]
    public TransactionType ttype;
}

public class TransactionType
{
    public TransactionType(){}
    public TransactionType(string txType) { this.type = txType; }

    [XmlText]
    public string type;

    [XmlAttribute("TransactionTypeCode")]
    public string typecode;
}

This code will initialize and serialize the way you want:

public void Run()
{
    XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
    ns.Add( "", "" );

    Transaction tx = new Transaction();
    tx.id = 1;
    tx.ttype.type = "Withdrawal";
    tx.ttype.typecode = "520"; 
    using (StringWriter o = 
          new StringWriter(CultureInfo.InvariantCulture))
    {
        XmlSerializer s = new XmlSerializer(typeof(Transaction));
        s.Serialize(o, tx, ns);
        Console.Write(o.ToString());
    }
}
Cheeso