Search code examples
c#xmlxmlreader

readElements XML with XmlReader and Linq


My aim is to read this xml file stream:

<?xml version="1.0" encoding="utf-16"?>
<events>
    <header>
        <seq>0</seq>
    </header>
    <body>
        <orderBookStatus>
            <id>100093</id>
            <status>Opened</status>
        </orderBookStatus>
        <orderBook>
            <instrumentId>100093</instrumentId>
            <bids>
                <pricePoint>
                    <price>1357.1</price>
                    <quantity>20</quantity>
                </pricePoint>
                <pricePoint>
                    <price>1357.0</price>
                    <quantity>20</quantity>
                </pricePoint>
                <pricePoint>
                    <price>1356.9</price>
                    <quantity>71</quantity>
                </pricePoint>
                <pricePoint>
                    <price>1356.8</price>
                    <quantity>20</quantity>
                </pricePoint>
            </bids>
            <offers>
                <pricePoint>
                    <price>1357.7</price>
                    <quantity>51</quantity>
                </pricePoint>
                <pricePoint>
                    <price>1357.9</price>
                    <quantity>20</quantity>
                </pricePoint>
                <pricePoint>
                    <price>1358.0</price>
                    <quantity>20</quantity>
                </pricePoint>
                <pricePoint>
                    <price>1358.1</price>
                    <quantity>20</quantity>
                </pricePoint>
                <pricePoint>
                    <price>1358.2</price>
                    <quantity>20</quantity>
                </pricePoint>
            </offers>
            <lastMarketClosePrice>
                <price>1356.8</price>
                <timestamp>2011-05-03T20:00:00</timestamp>
            </lastMarketClosePrice>
            <dailyHighestTradedPrice />
            <dailyLowestTradedPrice />
            <valuationBidPrice>1357.1</valuationBidPrice>
            <valuationAskPrice>1357.7</valuationAskPrice>
            <lastTradedPrice>1328.1</lastTradedPrice>
            <exchangeTimestamp>1304501070802</exchangeTimestamp>
        </orderBook>
    </body>
</events>

I created (based on the post here: http://blogs.msdn.com/b/xmlteam/archive/2007/03/24/streaming-with-linq-to-xml-part-2.aspx a function

public IEnumerable<XElement> readElements(XmlReader r, string matchName)
    {
        //r.MoveToContent();
        while (r.Read())
        {
            switch (r.NodeType)
            {
                case XmlNodeType.Element:
                    {
                        if (r.Name == matchName)
                        {
                            XElement el = XElement.ReadFrom(r) as XElement;
                            if (el != null)
                                yield return el;
                        } break;
                    }
            }
        }
    }

which I planned to use in the following way

            IEnumerable<XElement> xBids = readElements(xmlReader, "bids");
            publishPricePoint(xBids, "bids");
            IEnumerable<XElement> xOffers = readElements(xmlReader, "offers");
            publishPricePoint(xOffers, "offers");

where the method publishPricePoint looks like this:

    public void publishPricePoint(IEnumerable<XElement> ie, string side)
    {
        PricePoint p = new PricePoint();
        var ci = CultureInfo.InvariantCulture.Clone() as CultureInfo;
        ci.NumberFormat.NumberDecimalSeparator = ".";

        var bids = (from b in ie.Elements() select b).ToList();
        foreach (XElement e in bids)
        {

             p.price = decimal.Parse(e.Element("price").Value, ci);
             p.qty = int.Parse(e.Element("quantity").Value, ci);
             OnPricePointReceived(this, new MessageEventArgs(p, side));
        }
    }

The problem is, that in this piece of code:

            IEnumerable<XElement> xBids = readElements(xmlReader, "bids");
            publishPricePoint(xBids, "bids");
            IEnumerable<XElement> xOffers = readElements(xmlReader, "offers");
            publishPricePoint(xOffers, "offers");

only first two lines work, ie. only bids can be read, not the offers. What is wrong with this? For me, it looks like, there XmlReader disappears after bids have been read. Thank you for help

================== Other solution =================

  while (xmlReader.Read())
  {
  #region reading bids
            if (xmlReader.IsStartElement("bids"))
            {
                readingBids = true;
                readingOffers = false;
            }

            if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.Name == "bids")
            {
                readingBids = false;
                readingOffers = false;
            }

            if (readingBids == true)
            {
                if (xmlReader.IsStartElement("price"))
                    price = xmlReader.ReadElementContentAsDecimal();

                if (xmlReader.IsStartElement("quantity"))
                {
                    qty = xmlReader.ReadElementContentAsInt();
                    OnPricePointReceived(this, new MessageEventArgs(price, qty, "bid"));
                }
            }
            #endregion

            #region reading offers
            if (xmlReader.IsStartElement("offers"))
            {
                readingBids = false;
                readingOffers = true;
            }

            if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.Name == "offers")
            {
                readingBids = false;
                readingOffers = false;
            }

            if (readingOffers == true)
            {
                if (xmlReader.IsStartElement("price"))
                    price = xmlReader.ReadElementContentAsDecimal();

                if (xmlReader.IsStartElement("quantity"))
                {
                    qty = xmlReader.ReadElementContentAsInt();
                    OnPricePointReceived(this, new MessageEventArgs(price, qty, "offer"));
                }
            }
 }

Solution

  • Why dont you do something like this

    XDocument document = XDocument.Load(@"XMLFile1.xml"); 
    
        var bidResults = (from br in document.Descendants("bids") 
                           select br).ToList();  
        var offerResults = (from or in document.Descendants("offers") 
                           select or).ToList(); 
    

    then you can just iterate with a foreach (Xelement element in bidResults) to get all the data of the bids and also the data from the offers

    foreach (XElement xElement in returnResult) 
       {       
        Offer off = new Offer();  
        off.price = xElement.Element("price") != null ? xElement.Element("price").Value : "";       
        off.quantity = xElement.Element("quantity") != null ? xElement.Element("quantity").Value : "";            
      }