【问题标题】:Serialising dictionary to XML将字典序列化为 XML
【发布时间】:2015-05-21 04:39:08
【问题描述】:

我需要准确地生成以下 XML。请注意,每个输入元素必须是字符串或布尔值。

<Userdata version="1.00">
   <ISKeyValueList>
      <Item type="String" key="AgeOfDependents">8,6,1<Item/>
      <Item type="Boolean" key="SecuritiesInPosession"> True </Item>
      <Item type="Boolean" key="SecuritiesOwners"> True </item>
   </ISKeyValueList>
</Userdata> 

除了输入元素之外,我可以正确生成上述大部分 XML。我当前的方法生成以下内容:

<Item type="String" key="AgeOfDependents"/>

如您所见,Item 元素不包含文本值 8,6,1。

我目前正在使用以下方法对数据进行序列化:

对象模型

public class finClient
{
    [XmlAttribute("version")]
    public string version = "1.00";
    public UserData Userdata;
}

public class UserData
{
    [XmlAttribute("version")]
    public string version = "1.00";
    public List<Item> ISKeyValueList;
}

public class Item
{
    [XmlAttribute("type")]
    public string type;
    [XmlAttribute("key")]
    public string key;
}

C#

以下是我如何构造稍后序列化为 XML 的对象

 Userdata = new UserData()
 {
    ISKeyValueList = new List<Item>()
    {
        new Item()
        {
           type = "String", key = "AgeOfDependents"
        }
    }   
 }

我知道向对象模型中的 Item 对象添加另一个元素(例如 Value)将允许我存储这些值,但这会将另一个元素添加到 XML 中,这无济于事,因为 XML 需要完全相同如最顶部所示。

我对这个问题的研究使我相信我需要使用字典。我认为我需要字典是否正确?如果是这样,我该如何通过对象模型序列化字典。

非常感谢任何可以提供帮助的人。

【问题讨论】:

  • 请提供可靠地重现问题的a good, minimal, complete code example。请注意,尽管这个问题表面上是关于 XML 序列化的,但您并没有显示任何实际序列化任何数据的代码!此外,您的代码示例中没有任何内容表明存在任何文本或其他读取“8,6,1”的值。为什么您希望它出现在 XML 中?
  • 这就是我要添加的内容。我没有提供序列化数据的代码,因为该代码没有问题。问题在于对象模型。
  • 所以你不在乎内存中的对象类型是什么样的?那么我会说已经给出的答案就足够了。如果没有,那么显然您的问题中遗漏了一些细节。

标签: c# xml dictionary xml-serialization object-model


【解决方案1】:

更新

鉴于您的Item 类可以包含不同类型的原始数据,我建议将其建模为包含IConvertible 类型的对象——即可以从字符串转换为字符串的对象。然后,您可以将该字符串值序列化为每个 Item 元素的 character data(在您的示例中为 8,6,1),方法是使用 [XmlTextAttribute] 属性装饰相应的属性:

public class Item
{
    TypeCode _type = TypeCode.Empty; // When deserializing, attributes are deserialized before element values, which allows the _type to be set before the XmlValue is read.
    IConvertible _value = null;

    static TypeCode GetItemType(IConvertible value)
    {
        if (value == null)
            return TypeCode.Empty;
        return value.GetTypeCode();
    }

    [XmlAttribute("type")]
    public TypeCode type
    {
        get
        {
            return _type;
        }
        set
        {
            _type = value;
            if (GetItemType(_value) != _type)
                _value = null;
        }
    }

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

    [XmlIgnore]
    public IConvertible Value
    {
        get
        {
            return _value;
        }
        set
        {
            _type = GetItemType(value);
            _value = value;
        }
    }

    [XmlText]
    public string XmlValue
    {
        get
        {
            if (_value == null)
                return null;
            return Value.ToString(CultureInfo.InvariantCulture);
        }
        set
        {
            if (value == null)
                _value = null;
            else
            {
                _value = (IConvertible)Convert.ChangeType(value, _type, CultureInfo.InvariantCulture);
            }
        }
    }
}

然后是下面的测试用例:

        string xml = @"<Userdata version=""1.00"">
           <ISKeyValueList>
              <Item type=""String"" key=""AgeOfDependents"">8,6,1</Item>
              <Item type=""Boolean"" key=""SecuritiesInPosession""> True </Item>
              <Item type=""Boolean"" key=""SecuritiesOwners""> True </Item>
           </ISKeyValueList>
        </Userdata>
        ";

        var userdata = xml.LoadFromXML<UserData>();
        Debug.WriteLine(userdata.GetXml(true));

产生以下输出:

<Userdata version="1.00">
    <ISKeyValueList>
        <Item type="String" key="AgeOfDependents">8,6,1</Item>
        <Item type="Boolean" key="SecuritiesInPosession">True</Item>
        <Item type="Boolean" key="SecuritiesOwners">True</Item>
    </ISKeyValueList>
</Userdata>

原答案

您可以通过使用[XmlTextAttribute] 属性装饰相应的属性,将字符串(或其他原始类型或枚举类型)序列化为元素的character data(在您的示例中为8,6,1)。这允许您将额外的 Value 属性添加到您的 Item 类中,如下所示:

public class Item
{
    [XmlAttribute("type")]
    public string type;
    [XmlAttribute("key")]
    public string key;

    [XmlIgnore] // Do not output the list to XML directly
    public List<int> Values { get; set; }

    [XmlText]  // Instead, output the list as a comma-separated string in the XML element's text value.
    public string XmlValue
    {
        get
        {
            if (Values == null)
                return null;
            return string.Join(",", Values.Select(i => XmlConvert.ToString(i)).ToArray());
        }
        set
        {
            if (value == null)
                Values = null;
            else
            {
                Values = value.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s => XmlConvert.ToInt32(s)).ToList();
            }
        }
    }
}

然后是下面的类:

var client = new finClient { Userdata = new UserData { ISKeyValueList = new List<Item> { new Item { type = "String", key = "AgeOfDependents", Values = new List<int> { 8, 6, 1 } } } } };

将被序列化为以下 XML:

<finClient version="1.00">
   <Userdata version="1.00">
      <ISKeyValueList>
         <Item type="String" key="AgeOfDependents">8,6,1</Item>
      </ISKeyValueList>
   </Userdata>
</finClient>

【讨论】:

  • 我实际上需要向 ISKeyValueList 添加几个 Item 元素,例如 True True 如何修改您的示例以接受布尔值和字符串。如果您查看 type 属性,我相信 8, 6, 1 必须是一个字符串。
  • 您能否更新您的问题以包含所有必需 XML 输出的示例?
  • 完成。在我的问题开始时,我已将其他输入元素添加到 XML 中。非常感谢。
  • @CodeMonkey - 答案已更新(在我意识到您已接受原始答案之前)。
  • 非常感谢您的帮助
猜你喜欢
  • 2021-08-19
  • 2010-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多