2011-05-04 10 views
6

Soy muy nuevo en el análisis XML y comencé a aprender sobre linq, que creo que podría ser la mejor solución aquí. Estoy muy interesado en el rendimiento ya que la aplicación que estoy creando leerá los precios de las bolsas de valores, que a veces pueden cambiar muy rápidamente. que recibir mensaje desde el servidor:¿Cuál es la forma más rápida/más eficiente de leer este XML en el diccionario (Linq u otra cosa?)

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

Mi objetivo es analizar los elementos puntuales precio

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

en el diccionario de la siguiente estructura:

Dictionary<double, PriceLevel> 

donde el precio debería ser una double y PriceLevel es una clase

class PriceLevel 
{ 
    int bid; 
    int offer; 

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


} 

Dependiendo del elemento, en el que cada punto de precio exista (ofertas o ofertas) la cantidad debe asignarse en consecuencia, es decir, si el precio existe en ofertas, entonces la cantidad debe asignarse a la oferta y 0 ofrecerse. Por el contrario, si el punto de precio existe en las ofertas, entonces la cantidad debe asignarse a la oferta y 0 a la oferta.

Espero que mi explicación sea clara; sin embargo, si tiene algún problema para comprenderla, no dude en solicitar una aclaración en los comentarios. Agradecería mucho ayuda para resolver este problema.

+++++++++++++++++++++++++++++++++++++++++ Actualización:

He profundizado en la transmisión que trato de leer, y no va a ser tan simple como esperaba. Descubrí que la transmisión no siempre contiene el documento completo, por lo tanto, tendré que leerlo con XmlReader para procesar la transmisión continuamente. En este caso, ¿cómo puedo leer ofertas y ofertas? Tengo algo como esto:

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)); 
       } 
      } 
     } 

pero estoy luchando para enlazar nombre del elemento a su valor ... es decir, ¿cómo puedo saber qué punto de precio de la oferta que estoy leyendo actualmente y si este existe en licitaciones u ofertas? Gracias por su ayuda

+0

¿Cuál es la precisión máxima de su punto de precio? los tiene todos con 1 lugar decimal, con 4 dígitos antes del dp, en su ejemplo. ¿Es así como será para todos sus puntos de precio? –

+0

¿Qué tan rápido debe ser el más rápido? Segundos, milisegundos, microsegundos? – Ishtar

+0

Creo que esa precisión máxima de 6 decimales debería ser suficiente. en cuanto a la velocidad y el rendimiento, habrá muchos mensajes similares por segundo, así que estoy hablando de milisegundos al menos;) – Macin

Respuesta

3

Cuando se está usando una interfaz basada en eventos, similar a la presentada en su actualización, deberá recordar el nombre del evento del elemento de inicio anterior. A menudo vale la pena mantener una pila para realizar un seguimiento de los eventos. Probablemente haga algo similar a lo siguiente:

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; 
     } 
    } 
} 
+0

Gracias Michael, ¡esta es una respuesta muy útil! – Macin

0

En primer lugar, creo que su método de poner en el diccionario daría lugar a un error. Si no está mal, el diccionario no puede tener la misma clave, por lo que, dado que está utilizando el precio como clave, habrá una gran posibilidad de que llegue a este problema.

No puedo decir por la velocidad, tienes que probar. Pero hasta ahora XDocument funciona bien para mí.
Usando XDocument, cargue todo el mensaje XML en esa variable, por ejemplo

XDocument doc = XDocument.Load(message); 

Con doc, puede utilizar LINQ agruparlos en oferta y demanda.

Una vez que lograr esto, no debería haber ningún problema en la presentación de datos a medida que ya tiene el precio y los separó en oferta y demanda

+0

+1 por colisión de clave, -1 por sugerir XDocument, sin saber si es más eficiente. –

2

primero que necesita para obtener todas las ofertas y todas las ofertas

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(); 

luego, itera las ofertas y ofertas a gran velocidad y las agrega al diccionario ... pero como alguien dijo antes ... tal vez tenga el problema de que un nivel de precio tendrá ofertas y ofertas establecidas si tienen el mismo precio

para iterar throgugh el l IST que acaba de hacer esta

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

el mismo que hacer por la oferta ... pero de nuevo .. .you probablemente tendrá que comprobar si esta clave ya existe ...

0

He conseguido algo como esto :

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(); 
    } 
} 

¿Es esta la solución adecuada para este problema? Tengo algunas dudas con respecto a este trozo de código:

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

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

sólo yo fuego evento OnPricePointReceived cuando me las arreglé para leer precio y cant. Sin embargo, existe la posibilidad de que no haya una cantidad para el precio dado (o no). ¿Cómo implementar la validación, para evitar errores basados ​​en mensajes incompletos?

Cuestiones relacionadas