简体   繁体   中英

C# XMLSerializer and sequence of attribute serialization

I have a problem with XML deserialization. Well, lets suppose that my class have three properties:

public class Order
{
    private string name;
    [XmlAttribute("Name")]
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            if (name != value)
            {
                name = value;
            }
        }
    }

    private string tid;
    [XmlAttribute("TID")]
    public string TID
    {
        get
        {
            return tid;
        }
        set
        {
            if (tid != value)
            {
                tid = value;
            }
        }
    }


    private Command command;
    [XmlAttribute("Command")]
    public string Command
    {
        get
        {
            return command.Name;
        }
        set
        {
            command = new Command(TID, Name, value);
        }
    }

    public Order()
    {

    }
}

As you can see, I need to create an object Command with parameter constructor on deserialize Command property - but I need TID and Name attribute to this. I need to be sure that these fields are not null. As I observed, XmlSerializer is deserializing attributes according to sequence how user write it. For example:

<Order Command="SetPlus" TID="W403" Name="SomeName" />

In this case, first serialized will be Command attribute. But I don't have TID and Name property yet! I can't trust user that he will pass attributes in correct sequence like:

<Order TID="W403" Name="SomeName" Command="SetPlus" />

Well, I need some event or something I can call on deserialize finish OR I need to be sure that XmlSerializer will deserialize attributes in sequence like I want. The only thing I found is:

[XmlElement(Order = 1)]

or

[XmlElementAttribute(Order = 1)]

But it doesn't work with XmlAttribute.

Is some way to do this? Hope I explained my problem clearly.

If your Command only depends on the TID and the Name why do you even serialize it? This is just redundant information.

You should use the [XmlIgnore] attribute on the Command property. After that you can implement a kind of lazy load method for your Command property.

By the way: Your code is wrong anyway. On the set part of your Command property, you don't even use the value the user passed in. It would be better if you'd do it like this:

private Command command;
[XmlIgnore]
public string Command
{
    get
    {
      if (command == null)
      {
        command = new Command(TID, Name));
      }
      return command.Name;
    }
}

edit:

ok after you changed your post and actually use the value of the Command property setter, the solution is a little trickier and there are multiple ways.

What I'd probably do:

  • Add another property called CommandName to your class, which just is a simple string property (with getter), also add and XmlAttribute to this property
  • Add the [XmlIgnore] to your Command property, and take my code from above (lazy initialization), return the command using the new CommandName property ( new Command(TID, Name, CommandName) )

Personally I would even add a setter to the CommandName property and let the Command property actually return a Command and not a string . In my opinion this would make the code cleaner and more readable.

For clarification, this is how my class would look like:

public class Order
{
    //todo: if Name, TID or CommandName changes you'd have to initialize a new Command objects with the new values

    [XmlAttribute("Name")]
    public string Name {get;set;}

    [XmlAttribute("TID")]
    public string TID {get;set;}

    [XmlAttribute("CommandName")]
    public string CommandName {get;set;}

    private Command command;
    [XmlIgnore]
    public Command Command
    {
      get
      {
        return command ?? (command = new Command(TID, Name, CommandName));
      }
    }
    public Order()
    {

    }
}

Can you put some logic in the setters so that when all the setters have been called you then create the command like so

public class Order
{
    private string name;
    [XmlAttribute("Name")]
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            if (name != value)
            {
                name = value;
                SetupCommandIfPossible();
            }

        }
    }

    private string tid;
    [XmlAttribute("TID")]
    public string TID
    {
        get
        {
            return tid;
        }
        set
        {
            if (tid != value)
            {
                tid = value;
                SetupCommandIfPossible();
            }
        }
    }


    private Command command;
    [XmlAttribute("Command")]
    public string Command
    {
        get
        {
            return command.Name;
        }
        set
        {
            SetupCommandIfPossible();
        }
    }

    public void SetupCommandIfPossible()
    {
         if (!string.IsNullOrEmpty(tid) && !string.IsNullOrEmpty(name) && command == null)
         {             
            command = new Command(TID, Name);
         }
    }

    public Order()
    {

    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM