What is the fastest / most efficient way to read this XML dictionary (Linq or something else?)

I am very new to XML parsing and I started to learn linq, which I think might be the best solution here. I'm more interested in performance, because the application I create will read stock prices, which can sometimes change very quickly. I get the following message from the server:

<?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> 

My goal is to analyze the elements of the price category

 <pricePoint> <price>1358.2</price> <quantity>20</quantity> </pricePoint> 

into the dictionary of the following structure:

 Dictionary<double, PriceLevel> 

where the price should be double, and PriceLevel - class

 class PriceLevel { int bid; int offer; public PriceLevel(int b, int o) { bid = b; offer = o; } } 

Depending on the element in which each price point exists (or bids or offers), the quantity must be assigned accordingly, that is, if there is a price point in the bids, then the quantity must be assigned for the bid and 0 for the offer. In contrast, if there is a price point in offers, then a quantity must be assigned for the offer and 0 for the bid.

I hope that my explanations are clear, but if you have any problems understanding this issue, please feel free to ask for clarification in the comments. I would really appreciate help in solving this problem.

++++++++++++++++++++++++++++++++++++++++++++ Update:

I delved into the stream that I am trying to read, and it will not be as simple as I expected. I found out that the stream will not always contain the entire document, so I will have to read it using XmlReader to process the stream continuously. In this case, how can I read bids and offers? I have something like this:

StreamReader sr = new StreamReader (".. \ .. \ videos.xml");

  XmlReader xmlReader = XmlReader.Create(sr); while (xmlReader.Read()) { if (xmlReader.HasValue) { OnXmlValue(this, new MessageEventArgs(true, xmlReader.Value));//saxContentHandler.Content(xmlReader.Value); } else { if (xmlReader.IsEmptyElement) { OnStartElement(this, new MessageEventArgs(false, xmlReader.Name)); OnEndElement(this, new MessageEventArgs(false, xmlReader.Name)); } else if (xmlReader.IsStartElement()) { OnStartElement(this, new MessageEventArgs(false, xmlReader.Name)); } else { OnEndElement(this, new MessageEventArgs(false, xmlReader.Name)); } } } 

but I'm struggling to associate the element name with its value ... i.e. How can I find out what point of the price of the offer I am reading, and if it exists in bids or offers? Thank you for your help.

+6
source share
4 answers

When using an event-based interface similar to the one that was specified in your update, you will need to remember the name of the previous event of the start element. It is often worth keeping a stack to track events. I would probably do something similar to the following:

 public class PriceLevel { private decimal? bid = null; private decimal? offer = null; public decimal? Bid { get { return bid; } set { bid = value; } } public decimal? Offer { get { return offer; } set { offer = value; } } } public delegate void OnPriceChange(long instrumentId, Dictionary<decimal, PriceLevel> prices); public class MainClass { private Stack<String> xmlStack = new Stack<String>(); private Dictionary<decimal, PriceLevel> prices = new Dictionary<decimal, PriceLevel>(); private bool isBids = false; private decimal? currentPrice = null; private long instrumentId; private OnPriceChange _priceChangeCallback; public void MainClass(OnPriceChange priceChangeCallback) { this._priceChangeCallback = priceChangeCallback; } public void XmlStart(object source, MessageEventArgs args) { xmlStack.Push(args.Value); if (!isBids && "bids" == args.Value) { isBids = true; } } public void XmlEnd(object source, MessageEventArgs args) { xmlStack.Pop(); if (isBids && "bids" == args.Value) { isBids = false; } // Finished parsing the orderBookEvent if ("orderBook" == args.Value) { _priceChangeCallback(instrumentId, prices); } } public void XmlContent(object source, MessageEventArgs args) { switch (xmlStack.Peek()) { case "instrumentId": instrumentId = long.Parse(args.Value); break; case "price": currentPrice = decimal.Parse(args.Value); break; case "quantity": if (currentPrice != null) { decimal quantity = decimal.Parse(args.Value); if (prices.ContainsKey(currentPrice)) { prices[currentPrice] = new PriceLevel(); } PriceLevel priceLevel = prices[currentPrice]; if (isBids) { priceLevel.Bid = quantity; } else { priceLevel.Offer = quantity; } } break; } } } 
+3
source

first you need to get all offers and all bets

 XDocument xmlDoc = XDocument.Load("TestFile.xml"); var bids = (from b in xmlDoc.Descendants("bids") select b).ToList(); var offers = (from o in xmlDoc.Descendants("offers") select o).ToList(); 

then you just repeat the bids and offers and add them to the dictionary ... but, as someone says earlier ... you may have a problem that the price level will have both bids and offers if they have the same price

to iterate through the list you just made.

 foreach (XElement e in bids) { price = e.Element("price").Value; quantity = e.Element("quantity").Value; dictionary.add(price, new PriceLevel(quantity,null); } 

the same thing you do for the sentence ... but again ... you probably need to check if this key exists ...

+2
source

First of all, I believe that your dictionary entry method will result in an error. If not, the dictionary cannot have the same key, so since you are using price as the key, you will have a very high probability that you will hit this problem.

I can not say about speed, you need to check. But for now, XDocument works great for me.
Using XDocument, load the entire XML message into this variable, for example

 XDocument doc = XDocument.Load(message); 

With doc, you can use Linq to group them into a request and ask.

Once you achieve this, there should be no problem presenting your data, since you have already received the price and divided it into a bid and ask

0
source

I managed to get something like this:

 public void messageParser() { int i = 0; bool readingBids = false; bool readingOffers = false; decimal price=0; int qty = 0; StreamReader sr = new StreamReader("..\\..\\sampleResponse.xml"); XmlReader xmlReader = XmlReader.Create(sr); DateTime startTime = DateTime.Now; 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")); } } #endregion } DateTime stopTime = DateTime.Now; Console.WriteLine("time: {0}",stopTime - startTime); Console.ReadKey(); } } 

Is this the correct solution to the problem? I have some doubts about this piece of code:

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

I only fire the OnPricePointReceived event when I managed to find out the price and quantity. However, there is a possibility that for a given price there will be no quantity (or not). How to implement valiadation to avoid errors based on incomplete messages?

0
source

Source: https://habr.com/ru/post/887262/


All Articles