【问题标题】:Deserialize dynamic XML反序列化动态 XML
【发布时间】:2016-02-22 13:45:21
【问题描述】:

下面的XML 总是以这种格式出现,但<Hit> 节点下的元素是动态的,每次的名称或项数可能不同。是否可以动态获取<Hit>节点下的元素。

<SearchResponse>
    <Response>
        <Qtime>3</Qtime>
        <HitsPerPage>10</HitsPerPage>
    </Response>
    <HitsCount>
        <total>33</total>
        <start>0</start>
        <end>10</end>
    </HitsCount>
    <Hits>
        <Hit>
            <id type=''>123</id>
            <eid type=''>456</eid>
            <Title type='t'>
                <![CDATA[title goes here]]>
            </Title>
        </Hit>
        <Hit>
            <id type=''>123</id>
            <oid type=''>456</oid>
            <Title type='t'>
                <![CDATA[title goes here]]>
            </Title>
            <Description type='s'>
                <![CDATA[Description goes here]]>
            </Description>
        </Hit>
    </Hits>
</SearchResponse>

编辑:这是 C# 代码,它工作正常,并从 &lt;Hit&gt; 节点获取 id,因为我已经定义了属性,但我需要将它们全部动态化。

[XmlRoot("SearchResponse")]
    public sealed class SearchResponse {
        [XmlElement("Response", Type = typeof(Response))]
        public Response[] Responses { get; set; }

        [XmlElement("HitsCount", Type = typeof(HitsCount))]
        public HitsCount[] HitsCount { get; set; }

        [XmlElement("Hits", Type = typeof(Hits))]
        public Hits[] Hits { get; set; }


        public static SearchResponse GetSearchResponseObject(string xmlString) {
            try {
                var reader = new StringReader(xmlString);
                var serializer = new XmlSerializer(typeof(SearchResponse));
                var instance = (SearchResponse)serializer.Deserialize(reader);

                return instance;
            } catch (Exception ex) {
                var asd = ex.Message;
                return null;
            }
        }
    }

    [Serializable]
    public class Response {
        [XmlElement("Qtime")]
        public string Qtime { get; set; }

        [XmlElement("HitsPerPage")]
        public string HitsPerPage { get; set; }

    }

    [Serializable]
    public class HitsCount {
        [XmlElement("total")]
        public string Total { get; set; }

        [XmlElement("start")]
        public string Start { get; set; }

        [XmlElement("end")]
        public string End { get; set; }
    }

    [Serializable]
    public class Hits {

        [XmlElement("Hit")]
        public Hit[] Hit { get; set; }
    }

    [Serializable]
    public class Hit {
        [XmlElement("id")]
        public string Id { get; set; }
    }

编辑2://命中类代码

public class Hit {

        // Since "id" is expected in the XML, deserialize it explicitly.
        [XmlElement("id")]
        public string Id { get; set; }

        private readonly List<XElement> _elements = new List<XElement>();

        [XmlAnyElement]
        public List<XElement> Elements { get { return _elements; } }

    }

【问题讨论】:

  • 您有此文档的架构吗?
  • 使用 linq-to-xml 应该很容易。只需查询 'Hit' XElements,循环通过它们查询子 XElements。
  • @DanField 不,没有为文档指定架构

标签: c# xml xml-serialization


【解决方案1】:

由于您不知道Hit 类中可能存在哪些元素,您可以向您的类添加List&lt;XElement&gt; 属性并将[XmlAnyElement] 属性附加到它。然后它将为该类捕获 XML 中的任何和所有未知元素。元素反序列化后,您可以添加 API 属性来查询具有特定名称的元素,例如:

public class Hit
{
    // Since "id" is expected in the XML, deserialize it explicitly.
    [XmlElement("id")]
    public string Id { get; set; }

    private readonly List<XElement> elements = new List<XElement>();

    [XmlAnyElement]
    public List<XElement> Elements { get { return elements; } }

#region convenience methods

    public string this[XName name]
    {
        get
        {
            return Elements.Where(e => e.Name == name).Select(e => e.Value).FirstOrDefault();
        }
        set
        {
            var element = Elements.Where(e => e.Name == name).FirstOrDefault();
            if (element == null)
                Elements.Add(element = new XElement(name));
            element.Value = value;
        }
    }

    const string title = "Title";

    [XmlIgnore]
    public string Title
    {
        get
        {
            return this[title];
        }
        set
        {
            this[title] = value;
        }
    }

#endregion
}

顺便说一句,如果您将Hits 数组标记为[XmlArray] 而不是[XmlElement],则可以消除Hits 类,如下所示:

[XmlRoot("SearchResponse")]
public sealed class SearchResponse
{
    [XmlElement("Response", Type = typeof(Response))]
    public Response[] Responses { get; set; }

    [XmlElement("HitsCount", Type = typeof(HitsCount))]
    public HitsCount[] HitsCount { get; set; }

    [XmlArray("Hits")] // Indicates that the hits will be serialized with an outer container element named "Hits".
    [XmlArrayItem("Hit")] // Indicates that each inner entry element will be named "Hit".
    public Hit [] Hits { get; set; }
}

更新

    public string this[XName name] { get; set; }

indexer。见Using Indexers (C# Programming Guide)。我添加了它,以便轻松执行以下操作:

var description = hit["Description"];
var title = hit["Title"];

索引器查找具有指定名称的第一个 XML 元素,并返回其 text value。如果你不想要它,你可以把它去掉——这只是为了方便。

【讨论】:

  • 谢谢,这工作得很好,但我不太确定“Hit”类中发生了什么。我对您的代码进行了一些更改,它仍然可以正常工作。您能否解释一下为什么要使用这部分代码 'public string this[XName name]' ,请查看我的问题中的最新更新以查看当前代码的更改。
猜你喜欢
  • 2011-05-03
  • 2012-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-29
  • 1970-01-01
相关资源
最近更新 更多