简体   繁体   English

阅读此 XML 到字典(Linq 或其他东西?)的最快/最有效的方法是什么?

[英]What is the fastest/most efficient way to read this XML to Dictionary (Linq or something else?)

I am very new to parsing XML, and I started learning about linq which I think might be the best solution here.我对解析 XML 非常陌生,我开始学习 linq,我认为这可能是这里最好的解决方案。 I am mostly interested in performance as the application I am creating will read stock exchange prices, which sometimes can change very rapidly.我最感兴趣的是性能,因为我正在创建的应用程序将读取股票交易价格,有时价格变化会非常迅速。 I receive 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 aim is to parse price point elements我的目标是解析价格点元素

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

into dictionary of the following structure:进入以下结构的字典:

Dictionary<double, PriceLevel> 

where price should be a double and PriceLevel is a class其中价格应该是双倍,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 (either bids or offers) quantity should be assigned accordingly, ie if price point exists in bids, then quantity should be assigned to bid, and 0 to offer.根据每个价格点所在的元素(投标或报价),应相应地分配数量,即,如果投标中存在价格点,则应将数量分配给投标,将 0 分配给报价。 On the opposite, if price point exists in offers, then quantity should be assigned to offer and 0 to bid.相反,如果报价中存在价格点,则应将数量分配给报价,将 0 分配给投标。

I hope my explanation is clear, however if you have any problems understanding it, please do not hesitate to ask for clarification in comments.我希望我的解释很清楚,但是如果您对理解有任何疑问,请随时在评论中要求澄清。 I would greatly appreciate help in solving this problem.我将不胜感激帮助解决这个问题。

+++++++++++++++++++++++++++++++++++++++++ Update: +++++++++++++++++++++++++++++++++++++++++++++ 更新:

I have gone deeper into the stream I am trying to read, and it is not going to be as simple as I expected.我已经深入研究了我正在尝试阅读的 stream,它不会像我预期的那么简单。 I found out, that the stream will not always contain the whole document, therefore I will have to read it using XmlReader to process the stream on the ongoing basis.我发现,stream 并不总是包含整个文档,因此我必须使用 XmlReader 读取它以持续处理 stream。 In this case, how can I read bids and offers?在这种情况下,我如何阅读出价和出价? I have something like this:我有这样的事情:

StreamReader sr = new StreamReader("..\..\videos.xml"); 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 am struggling to link element name to its value... ie, how can I know which bid price point I am currently reading and if this exists in bids or offers?但我正在努力将元素名称与其值联系起来......即,我如何知道我当前正在阅读的投标价格点以及它是否存在于投标或报价中? Thank you for help谢谢你的帮助

When are are using a event based interface, similar to the one presented in your update, you will need to remember the name of the previous start element event.何时使用基于事件的界面,类似于更新中显示的界面,您需要记住前一个开始元素事件的名称。 Often it is worth while holding a stack to keep track of the 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;
        }
    }
}

first you need to get all offers and all bids首先,您需要获得所有报价和所有出价

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 iterate throgh bids and offers and add them to the dictionary... but as someone sait before... you will maybe have the problem that an pricelevel will have both bids and offers set if they have the same price然后您只需遍历出价和出价并将它们添加到字典中...但是正如有人之前所说...您可能会遇到这样的问题,即如果价格水平相同,则出价和出价都会设置

to iterate throgugh the list you just do this遍历列表,您只需执行此操作

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

the same you do for offer... but again.. .you probably have to check if this key already exists...与您提供的相同...但是再次...您可能必须检查此密钥是否已经存在...

1st of all, I believe your method of putting into dictionary would result in error.首先,我相信您输入字典的方法会导致错误。 If not wrong, dictionary cannot have the same key, so since you are using price as the key, there will be very high chance u hit this issue.如果没有错,字典不能有相同的键,所以由于你使用价格作为键,你很有可能遇到这个问题。

I can't say for the speed, you have to test out.我不能说速度,你必须测试一下。 But so far XDocument runs fine for me.但到目前为止,XDocument 对我来说运行良好。
Using XDocument, load the whole xml message into that variable, for instance例如,使用 XDocument,将整个 xml 消息加载到该变量中

XDocument doc = XDocument.Load(message);

With doc, you can use Linq to group them into bid and ask.使用 doc,您可以使用 Linq 将它们分组为出价和询价。

Once you achieve this, there should be no problem in presenting your data as you already got the price and separated them into bid and ask一旦你实现了这一点,展示你的数据应该没有问题,因为你已经得到了价格并将它们分成投标和要价

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 a proper solution for the problem?这是解决问题的正确方法吗? I have some doubts regarding 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 OnPricePointReceived event when I managed to read price and qty.当我设法读取价格和数量时,我只触发 OnPricePointReceived 事件。 However, there is possibility, that there will be no quantity for the the given price (or not).但是,给定价格(或没有)可能没有数量。 How to implement valiadation, to avoid errors based on incomplete messages?如何实施验证,以避免基于不完整消息的错误?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM