2010-02-24 17 views
5

Necesito permitir que un usuario avanzado ingrese una expresión XPath y le muestre los valores o nodos o atributos encontrados. En .Net Framework, System.Xml.XPath.Extensions se puede usar para llamar a XPathEvaluate, pero Silverlight no tiene este MSDN reference. ¿Alguien ha reescrito los métodos de extensión para usar en Silverlight? ¿Cuál es el mejor enfoque para tomar? ¿Por qué no están disponibles en Silverlight o en el kit de herramientas (vote on the issue here)?¿Cómo puedo evaluar una expresión XPath en Silverlight?

+0

+1 Y estoy de acuerdo en que me gustaría ver un puerto de los métodos de extensión yo mismo. – AnthonyWJones

+0

¿Un "Usuario Avanzado" no es un desarrollador? ¿Qué parte de la gama completa de XPath necesitaría ser compatible? – AnthonyWJones

+0

"Usuario avanzado" como en alguien que querría usar un Xpath en lugar del resto de la IU que estoy creando para ayudarlos a hacerlo. Entonces solo tendrían que ingresar el xPath y les mostraría el resultado. – Aligned

Respuesta

0

Una solución es utilizar un controlador genérico y subcontratar el procesamiento al servidor en una solicitud asincrónica. Aquí está un paso a paso:

Primero:

Cree un controlador genérico en su proyecto web. Añadir un archivo ASHX con el siguiente código para su ProcessRequest (simplificada por razones de brevedad):

public void ProcessRequest(HttpContext context) 
    { 
     context.Response.ContentType = "text/plain"; 

     string xml = context.Request.Form["xml"].ToString(); 
     string xpath = context.Request.Form["xpath"].ToString(); 

     XmlReader reader = new XmlTextReader(new StringReader(xml)); 
     XDocument doc = XDocument.Load(reader); 

     var rawResult = doc.XPathEvaluate(xpath); 
     string result = String.Empty; 
     foreach (var r in ((IEnumerable<object>)rawResult)) 
     { 
      result += r.ToString(); 
     } 

     context.Response.Write(result); 

    } 

Cabe señalar que hay algunos espacios de nombres que se necesitan referencias para el procesamiento de xml:

  1. System.IO

  2. System.XML

  3. System.Xml.XPat h

  4. System.Xml.Linq

Segundo:

Es necesario un código que le permitirá hacer un puesto asíncrona al controlador genérico. El código siguiente es largo, pero en esencia lo que pase el siguiente:

  1. El URI de su controlador genérico

  2. Un diccionario de pares de valores clave (suponiendo que el documento XML y el XPath)

  3. Devolución de llamada para el éxito y el fracaso

  4. Referencia al temporizador de envío del UserControl para que pueda acceder a su IU (si es necesario) en las devoluciones de llamada.

Aquí hay un código que he puesto en una clase de utilidad:

public class WebPostHelper 
{ 
    public static void GetPostData(Uri targetURI, Dictionary<string, string> dataDictionary, Action<string> onSuccess, Action<string> onError, Dispatcher threadDispatcher) 
    { 
     var postData = String.Join("&", 
         dataDictionary.Keys.Select(k => k + "=" + dataDictionary[k]).ToArray()); 

     WebRequest requ = HttpWebRequest.Create(targetURI); 
     requ.Method = "POST"; 
     requ.ContentType = "application/x-www-form-urlencoded"; 
     var res = requ.BeginGetRequestStream(new AsyncCallback(
      (ar) => 
      { 
       Stream stream = requ.EndGetRequestStream(ar); 
       StreamWriter writer = new StreamWriter(stream); 
       writer.Write(postData); 
       writer.Close(); 
       requ.BeginGetResponse(new AsyncCallback(
         (ar2) => 
         { 
          try 
          { 
           WebResponse respStream = requ.EndGetResponse(ar2); 
           Stream stream2 = respStream.GetResponseStream(); 
           StreamReader reader2 = new StreamReader(stream2); 
           string responseString = reader2.ReadToEnd(); 
           int spacerIndex = responseString.IndexOf('|') - 1; 
           string status = responseString.Substring(0, spacerIndex); 
           string result = responseString.Substring(spacerIndex + 3); 
           if (status == "success") 
           { 
            if (onSuccess != null) 
            { 
             threadDispatcher.BeginInvoke(() => 
             { 
              onSuccess(result); 
             }); 
            } 
           } 
           else 
           { 
            if (onError != null) 
            { 
             threadDispatcher.BeginInvoke(() => 
             { 
              onError(result); 
             }); 
            } 
           } 
          } 
          catch (Exception ex) 
          { 
           string data2 = ex.ToString(); 
          } 
         } 
        ), null); 

      }), null); 
    } 
} 

Tercero:

realizar la llamada a su clase de utilidad y pasar el código XML y XPath:

private void testButton_Click(object sender, RoutedEventArgs e) 
    { 
     Dictionary<string, string> values = new Dictionary<string, string>(); 
     values.Add("xml", "<Foo />"); 
     values.Add("xpath", "/*"); 

     //Uri uri = new Uri("http://eggs/spam.ashx"); 
     Uri uri = new Uri("http://localhost:3230/xPathHandler.ashx"); 

     WebPostHelper.GetPostData(uri, values, 
      (result) => 
      { 
       MessageBox.Show("Your result " + result); 
      }, 
      (errMessage) => 
      { 
       MessageBox.Show("An error " + errMessage); 
      }, 
      this.Dispatcher); 

    } 

Permítanme reiterar que el código aquí es simplificado para la brevedad. Es posible que desee utilizar un serializador para pasar tipos más complejos hacia y desde su controlador genérico. Tendrá que escribir "controles de cordura" de "guardia nula" de forma defensiva cuando obtenga los valores del contexto. Colección Request.Form. Pero la idea básica es como se documentó anteriormente.

+0

-1. En estos días, rara vez voto negativamente, pero me siento obligado a hacerlo en este caso. Tomando el XML que podría ser bastante grande, codificándolo en 'x-www-form-urlencoded' (con todo tipo de dolores de cabeza de codificación de caracteres que puedan crearse), restaurándolo todo en el servidor para ejecutar un XPath y devolver un resultado (que también podría ser grande) que, por lo que puedo ver, también genera un resultado incomprensible. – AnthonyWJones

+0

Anthony, tomo tu raro voto popular como un "punto bien tomado", sin embargo, te animo a que ofrezcas una solución. Un archivo xml puede ser bastante grande (por ejemplo, la biblioteca de iTunes) pero en muchas circunstancias son más pequeños por diseño (por ejemplo, alimentación RSS típica). La publicación de estilo AJAX con codificaciones de caracteres (Server.UrlDecode probablemente debería estar en la muestra) funciona bien (tome el código anterior e impleméntelo, podrá verlo usted mismo). – t3rse

1

Creo que el motivo por el que XPath no está disponible en Silverlight es que MS quiere que uses Linq en vez de XML. Pero eso no te ayuda exactamente. Desafortunadamente, creo que será difícil lograr lo que quieres. Si debe tener esta funcionalidad, creo que deberá recurrir a enviar su consulta al servidor, evaluarla allí y devolver el resultado. Es feo, pero creo que es la única forma.

+0

Dudo mucho que Microsoft le pida que use Linq. Es más probable que, en vista de querer mantener los tamaños de descarga al mínimo y el hecho de que Linq sea bastante bueno en hacer lo que se puede hacer con XPath, no se priorizó. – AnthonyWJones

+2

Resulta que la compatibilidad con XPath está disponible en Silverlight 4: http://programmerpayback.com/2010/04/01/xpath-support-in-silverlight-4-xpathpad/ –

0

Ahora parece haber a NuGet package que proporciona esta funcionalidad. El autor aparece como Microsoft.

Cuestiones relacionadas