4

我想尝试从我编写的 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....

但我只是不知道如何创建这个对象。这甚至可能吗?

4

5 回答 5

7

我认为你可以使用ExpandoObject类来近似这个,但很可能有更好的方法来做到这一点(比如使用更松散类型的语言、使用字典等)

.NET BCL 中当然没有内置任何内容来执行此操作。

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

于 2012-09-02T23:14:52.377 回答
2

你不能这样做,因为 C# 是一种强类型语言。也许以某种方式使用动态 C# 是可能的,但我对此知之甚少。但作为一种解决方案,您可以将 XML 解析为字典。

于 2012-09-02T23:03:02.267 回答
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。列表中的每个项目都有一个NameandValue属性,因此您可以检查它是什么。

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

foreach (var s in left.First().values)
{
    Console.WriteLine(string.Format("{0} has a value of {1}", s.Name, s.Value));            
}
于 2013-04-10T20:35:36.290 回答
0

一个月前建立了类似的东西,看看并告诉我你的想法:

对象到 XML 并返回到 Dynamics

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

于 2013-04-10T21:25:28.940 回答
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);
    }
}

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

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 表达式或 lambda 中使用 dynamic ,因此 return 没有多大意义IEnumerable<DynamicXml>

于 2013-04-14T00:57:31.020 回答