我如何定义我的类,使节点的xmltribute文本(而不是它的值)可以在运行时指定,而无需自定义序列化器



如何定义我的类结构,以便我可以在运行时为给定元素分配XmlAttribute文本(而不是它的值)?例如,我有以下XML,其中我想为下面标记为"DEFINE_UNIQUE_FOR_EACH_INSTANCE"的特定XML注释属性文本的每个实例定义一个唯一值:

<MyXml>
  <ZipCode DEFINE_UNIQUE_FOR_EACH_INSTANCE="Postal Code">90210</ZipCode>
  <State DEFINE_UNIQUE_FOR_EACH_INSTANCE="US State">CA</State>
</MyXml>

所以我想得到这样的东西:

<MyXml>
  <ZipCode labelText="Postal Code">90210</ZipCode>
  <State defaultValue="US State">CA</State>
</MyXml>
下面是定义上面提到的第一个XML的类定义:
[XmlRootAttribute]
public class MyXml
{
    public XmlValueAndAttribute ZipCode { get; set; }
    public XmlValueAndAttribute State { get; set; }
    public MyXml()
    { 
        ZipCode = new XmlValueAndAttribute(); State = new XmlValueAndAttribute();
    }
}

public class XmlValueAndAttribute
{
    [XmlAttribute("DEFINE_UNIQUE_FOR_EACH_INSTANCE")]
    public string AttributeValue { get; set; }
    [XmlText]
    public string Value { get; set; }
    public XmlValueAndAttribute() { }
    public XmlValueAndAttribute(string value, string attribute) 
    {
        Value = value;
        AttributeValue = attribute;
    }
}

和类的用法。注意注释掉的代码,说明我想如何进行属性文本分配:

static void Main(string[] args)
{
    MyXml xml = new MyXml();
    xml.ZipCode = new XmlValueAndAttribute("90210", "Postal Code" /*, "labelText"*/ )
    xml.State   = new XmlValueAndAttribute("CA", "US State" /*"defaultValue"*/);
    XmlSerializer x = new XmlSerializer(xml.GetType());
    var xmlnsEmpty = new XmlSerializerNamespaces();
    xmlnsEmpty.Add("", "");
    x.Serialize(Console.Out, xml, xmlnsEmpty);
    Console.ReadKey();
}

谢谢。

你可以让你的XmlValueAndAttribute类实现IXmlSerializable:

public class XmlValueAndAttribute : IXmlSerializable
{
    public string AttributeName { get; set; }
    public string AttributeValue { get; set; }
    public string Value { get; set; }
    public XmlValueAndAttribute() { }
    public XmlValueAndAttribute(string value, string attribute, string attributeName)
    {
        Value = value;
        AttributeValue = attribute;
        AttributeName = attributeName;
    }
    #region IXmlSerializable Members
    public XmlSchema GetSchema()
    {
        return null;
    }
    static XName nilName = XName.Get("nil", "http://www.w3.org/2001/XMLSchema-instance");
    public void ReadXml(XmlReader reader)
    {
        using (var subReader = reader.ReadSubtree())
        {
            var element = XElement.Load(subReader);
            reader.Read(); // Advance past the end of the element.
            if (element == null)
                return;
            Value = (bool?)element.Attribute(nilName) == true ? null : element.Value;
            var attr = element.Attributes().Where(a => a.Name != nilName && !a.IsNamespaceDeclaration).FirstOrDefault();
            if (attr != null)
            {
                AttributeName = XmlConvert.DecodeName(attr.Name.LocalName);
                AttributeValue = attr.Value;
            }
        }
    }
    public void WriteXml(XmlWriter writer)
    {
        if (!string.IsNullOrEmpty(AttributeName))
            writer.WriteAttributeString(XmlConvert.EncodeLocalName(AttributeName), AttributeValue);
        if (Value == null)
            writer.WriteAttributeString("xsi", nilName.LocalName, nilName.Namespace.ToString(), XmlConvert.ToString(true));
        else
            writer.WriteString(Value);
    }
    #endregion
}

请注意,此实现通过写入xsi:nil="true"来正确捕获Value属性的空值,但如果AttributeValue为空而AttributeName非空,则在反序列化时AttributeValue将被转换为空字符串。

如果您尝试用其他东西(如变量)替换DEFINE_UNIQUE_FOR_EACH_INSTANCE,则会收到以下错误:

属性参数必须是属性参数类型

的常量表达式、typeof表达式或数组创建表达式。

这清楚地表明,形参的值在编译时必须存在。因此,在我看来,似乎没有简单的方法,除了IL挥舞或其他hack。

最新更新