从未知的xml中创建匿名对象

时间:2012-09-02 22:57:34

标签: c# xml anonymous-types

我想尝试从我编写的XML阅读器返回一个对象来处理一个部分未知结构的文件。

以下是XML的示例。

<xml>
  <strings>
    <Home>
      <Index>
        <PreWrapper>
          <Left>
            <Title>Blah</Title>
            <Body>Lorem ipsum dolor sit amet, consec tetuer adipiscing elit. Praesent vestibulum molestie lacus. Aenean nonummy hendrerit mauris. Phasellus porta. Fusce suscipit</Body>
            <LinkText>read more</LinkText>
            <LinkUrl>#</LinkUrl>
          </Left>
          <Center>
            <Title>Exploit your ideas</Title>
            <Body>Lorem ipsum dolor sit amet, consec tetuer adipiscing elit. Praesent vestibulum molestie lacus. Aenean nonummy hendrerit mauris. Phasellus porta. Fusce suscipit</Body>
            <LinkText>read more</LinkText>
            <LinkUrl>#</LinkUrl>
          </Center>
          <Right>
            <Title>Grow your business</Title>
            <Body>Lorem ipsum dolor sit amet, consec tetuer adipiscing elit. Praesent vestibulum molestie lacus. Aenean nonummy hendrerit mauris. Phasellus porta. Fusce suscipit</Body>
            <LinkText>read more</LinkText>
            <LinkUrl>#</LinkUrl>
          </Right>
        </PreWrapper>
      </Index>
    </Home>
  </strings>
  <config>
    <Home>
      <Index>
        <ShowPreWrapper>True</ShowPreWrapper>
      </Index>
    </Home>
  </config>
</xml>

我希望能够与我的读者联系

var left = reader.GetObject("xml/strings/Home/Index/PreWrapper/Left");

我希望它返回一个匿名对象(或动态对象,但我不太了解那些),看起来像

left.Title
left.Body
left.LinkText
left.LinkUrl

var xml = reader.GetObjcet("xml");

并能够像

一样潜入它
xml.strings.Home.Index....

但我无法弄清楚如何创建这个对象。 这甚至可能吗?

5 个答案:

答案 0 :(得分:6)

我认为您可以使用ExpandoObject类进行近似,但很可能有更好的方法(例如使用更松散的类型语言,使用词典等)。

.NET BCL当然没有内置任何功能。

编辑:我最近发现了这一点,它可能适用于这种情况:ElasticObject

答案 1 :(得分:2)

你不能这样做,因为C#是一种强类型语言。也许有可能以某种方式使用动态C#,但我不太了解它。但作为解决方案,您可以将XML解析为字典。

答案 2 :(得分:0)

您可以使用LINQ to XML执行此操作如果您确定知道元素或属性名称。对于此示例,您知道将存在Left元素。

使用此LINQ语句

XDocument document = XDocument.Load("c:\\tmp\\test.xml");
var left = from i in document.Descendants("Left") 
           select new { Title = i.Element("Title").Value, 
                        Body = i.Element("Body").Value,
                        LinkText = i.Element("LinkText").Value, 
                        LinkUrl = i.Element("LinkUrl").Value
                      };

现在你可以这样做,如你的问题所述:

Left.Title
Left.Body
Left.LinkText
Left.LinkUrl

如果你不知道有多少元素是Left元素的后代,你可以这样做:

XDocument document = XDocument.Load("c:\\tmp\\test.xml");
var left = from i in document.Descendants("Left")
           select new { values = i.Elements().ToList() };

这给出了一个名为values的列表的可枚举。列表中的每个项目都有NameValue属性,因此您可以查看它是什么。

打印可枚举中第一项的所有值的示例:

foreach (var s in left.First().values)
{
    Console.WriteLine(string.Format("{0} has a value of {1}", s.Name, s.Value));            
}

答案 3 :(得分:0)

在一个月前制作类似的东西,看看并告诉我你的想法:

Objects to XML and back to Dynamics

(我希望我添加了所有内容)。

答案 4 :(得分:0)

如果您使用C#4.0,则可以使用动态类型来实现此目的。

要实现自己的动态行为,只需编写一个继承自DynamicObject并覆盖TryGetMember的类:

public sealed class DynamicXml : DynamicObject
{
    private XElement _xml;

    private DynamicXml(XElement xml)
    {
        _xml = xml;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        XElement xml = _xml.Element(binder.Name);
        if (xml == null)
        {
            result = null;
            return false;
        }

        result = new DynamicXml(xml);

        return true;
    }

    public static implicit operator XElement(DynamicXml xml)
    {
        return xml._xml;
    }

    public static explicit operator DynamicXml(XElement xml)
    {
        return new DynamicXml(xml);
    }
}

我添加了从DynamicXmlXElement的隐式转换以及从XElementDynamicXml的显式转换。这样您就可以轻松地在动态和静态之间切换:

XElement root = XDocument.Load("Test.xml").Element("xml");

dynamic xml = (DynamicXml)root;
XElement indexElement = xml.strings.Home.Index;

更新:

如果要访问属性,可以将其作为索引属性实现:

public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
    if (indexes.Length != 1 || !(indexes[0] is string))
    {
        result = null;
        return false;
    }

    result = _xml.Attribute(indexes[0] as string);

    return result != null;
}

var attr = xml.strings.Home.Index["id"]现在返回第一个id元素的Index属性。

此外,您可以使用不带参数的方法调用的语法来返回所有元素而不是仅返回第一个元素:

public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
    if (args.Length != 0)
    {
        result = null;
        return false;
    }

    result = _xml.Elements(binder.Name);

    return true;
}

var indexes = xml.strings.Home.Index()会返回IEnumerable<XElement>。既然你不能在LINQ表达式或lambdas中使用动态,那么返回IEnumerable<DynamicXml>就没有多大意义。