通过递归动态构建带有XElement的Xml

时间:2012-12-15 15:51:38

标签: c# xml linq reflection linq-to-xml

我是linq to Xml的新手 我有一个递归方法,它获取参数XElement root,它应该以一种表示给定递归深度的相关子树根的方式保存XML数据。

void recursiveMethod(XElement root);
更具体地说,还要看一下这个XML示例:

<start>
      <Class>
           <Worker>
                <Name> Dan </Name>
                <Phone> 123 </Phone> 
                <Class>
                      <Address>
                           <Street> yellow brick road </Street>
                           <Zip Code> 123456 </Zip Code>
                      </Address>
                </Class>
            </Worker>
      </Class>
...
</start>    

您可以想象,Name是值类型,而Address是类引用 应通过反射动态添加Xml信息(自顶向下方法)。

总而言之,想象一下,我正在调查Worker班级并达到Address班级并希望“向下钻取”,所以我想用我的递归方法来调用正确引用当前Worker类的子节点作为新的XElement根,因此我将能够在下面的Address一级递归深度中添加我通过反射找到的内容。

请注意,此引用应为XElement类型。

我该怎么做?

编辑:如果你有另外想法做所有这些事情而不是XElement,我会很高兴听到,尽管我更喜欢XElement参数。

另一个问题
我已经开始以一种天真的方式实现它,比如遍历所有字段(FieldInfo []的变量),如果我遇到了值类型(IsValueType),我就会做类似的事情

 root.Add(new XElement("Field",
                      new XElement("Type", ...),
                      new XElement("Variable Name", ...),
                      new XElement("Value", ...)));     

所以,仅仅是为了一般知识:
1.有没有办法只获得一个节点的引用到它的后代,这样在较低的递归级别,我将能够执行另一个root.Add(...),但是这个根将是一个引用以前的孩子? (这意味着在没有Linq语法的情况下进行整个操作)

2.我设法通过反射获得私有字段值而无需使用属性,这有问题吗?我应该总是通过反射中的属性获取值吗?

1 个答案:

答案 0 :(得分:5)

此扩展方法将以所需格式构建XElement:

public static class Extensions
{
    public static XElement ToXml<T>(this T obj)
    {
        Type type = typeof(T);

        return new XElement("Class",
                    new XElement(type.Name,
                        from pi in type.GetProperties()
                        where !pi.GetIndexParameters().Any()
                        let value = (dynamic)pi.GetValue(obj, null)
                        select pi.PropertyType.IsPrimitive || 
                               pi.PropertyType == typeof(string) ?
                                new XElement(pi.Name, value) : 
                                Extensions.ToXml(value)
                        )
                    );
    }
}

这里会发生什么:

  • 我们获取传递对象的公共属性(您可以将BindingFlags添加到过滤器属性)。
  • 接下来,我验证属性是否具有索引参数并跳过此类属性。
  • 接下来,我将属性值作为dynamic对象获取。这很重要,否则在递归调用object方法时,属性值类型将被推断为ToXml<T>
  • 我检查属性类型是基本类型(int,byte,long,single,double等)还是字符串
  • 对于原始类型,我们使用属性值编写元素。对于其他类型(复杂),我们递归地开始构建XElement

用法:

Worker worker = new Worker()
{
    Name = "Serge",
    Phone = "911",
    Address = new Address() { Street = "Elm street", ZipCode = 666 }
};

XElement xml = worker.ToXml();

结果:

<Class>
  <Worker>
    <Name>Serge</Name>
    <Phone>911</Phone>
    <Class>
      <Address>
        <Street>Elm street</Street>
        <ZipCode>666</ZipCode>
      </Address>
    </Class>
  </Worker>
</Class>

但是 你应该注意两个对象相互引用的情况(在这种情况下会发生无限递归)

在这种情况下,您可以使用类似字典或散列集的内容来存储xml中已存在的所有对象:

Type type = obj.GetType();
if (set.Contains(obj))
    return new XElement("Class", new XAttribute("name", type.Name));
set.Add(obj);
return new XElement("Class", ...);