2008-11-01 21 views
7

Esta es una pregunta muy complicada sobre cómo serializar datos a través de una llamada de servicio web, cuando los datos no están fuertemente tipados. Trataré de exponerlo lo mejor posible.Serialización de pares de nombre/valor en un objeto personalizado a través del servicio web

almacenamiento de objetos de la muestra:

[Serializable] 
public class StorageObject { 
    public string Name { get; set; } 
    public string Birthday { get; set; } 
    public List<NameValuePairs> OtherInfo { get; set; } 
} 
[Serializable] 
public class NameValuePairs { 
    public string Name { get; set; } 
    public string Value { get; set; } 
} 

la muestra Uso:

[WebMethod] 
    public List<StorageObject> GetStorageObjects() { 
     List<StorageObject> o = new List<StorageObject>() { 
     new StorageObject() { 
      Name = "Matthew", 
      Birthday = "Jan 1st, 2008", 
      OtherInfo = new List<NameValuePairs>() { 
      new NameValuePairs() { Name = "Hobbies", Value = "Programming" }, 
      new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" } 
      } 
     }, 
     new StorageObject() { 
      Name = "Joe", 
      Birthday = "Jan 10th, 2008", 
      OtherInfo = new List<NameValuePairs>() { 
      new NameValuePairs() { Name = "Hobbies", Value = "Programming" }, 
      new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" } 
      } 
     } 
     }; 

     return o; 
    } 

Valor de retorno del servicio Web:

<StorageObject> 
    <Name>Matthew</Name> 
    <Birthday>Jan 1st, 2008</Birthday> 
    <OtherInfo> 
     <NameValuePairs> 
      <Name>Hobbies</Name> 
      <Value>Programming</Value> 
     </NameValuePairs> 
     <NameValuePairs> 
      <Name>Website</Name> 
      <Value>Stackoverflow.com</Value> 
     </NameValuePairs> 
    </OtherInfo> 
</StorageObject> 

lo que quiero:

<OtherInfo> 
    <Hobbies>Programming</Hobbies> 
    <Website>Stackoverflow.com</Website> 
</OtherInfo> 

La Razón & Otras Cosas:

En primer lugar, lo siento por la longitud del poste, pero quería dar reproducible código también.

Lo quiero en este formato, porque estoy consumiendo los servicios web de PHP. Quiero ir fácilmente:

// Este es imporante

In PHP => "$Result["StorageObject"]["OtherInfo"]["Hobbies"]". 

Si está en otro formato, entonces no habría manera para mí para lograr eso, en absoluto. Además, en C# si estoy consumo del servicio, también me gustaría ser capaz de hacer lo siguiente:

// Este es imporante

In C# => var m = ServiceResult[0].OtherInfo["Hobbies"]; 

Por desgracia, no estoy seguro de cómo lograr esto . Pude obtenerlo de esta manera, construyendo un diccionario personalizado que implementó IXmlSerializer (vea StackOverflow: IXmlSerializer Dictionary), sin embargo, expulsó el esquema WSDL del agua. ¡También es demasiado complicado y produjo resultados horribles en mi aplicación WinFormsTester!

¿Hay alguna manera de lograr esto? ¿Qué tipo de objetos necesito crear? ¿Hay alguna forma de hacer esto/que no sea haciendo una colección fuertemente tipada /? Obviamente, si lo hago fuertemente tipado como esto:

public class OtherInfo { 
    public string Hobbies { get; set; } 
    public string FavoriteWebsite { get; set; } 
} 

Luego de que funcionaría perfectamente, lo habría hecho sin problemas WSDL, que sería capaz de acceder fácilmente a partir de PHP y C# (.OtherInfo.Hobbies) .

Sin embargo, perdería por completo el punto de NVP, en el sentido de que tendría que saber de antemano cuál es la lista, y sería inmutable ... por ejemplo, desde una base de datos.

¡Gracias a todos! Espero que podamos encontrar algún tipo de solución para esto.Aquí está son los requisitos más:

  1. esquema WSDL no debe romper
  2. pares de valores de nombre (de NVP) debe ser serializado a formato atributo
  3. debe ser fácil de acceder a la NVP de en PHP por su nombre [ "aficiones"]
  4. debe ser fácil de acceso en C# (y ser compatible con su generador de proxy)
  5. ser fácilmente serializable
  6. No me requieren para escribir los datos fuertemente

Ahora, estoy/completamente/abierto a la entrada de una manera mejor/diferente de hacer esto. Estoy almacenando información relativamente "estática" (como Nombre) y un montón de datos. Si hay una mejor manera, me encantaría escucharlo.

Respuesta

5

Esto es como propiedades dinámicas para un objeto. C# no es un lenguaje bastante dinámico a diferencia de JavaScript o tal vez PHP puede analizar las propiedades del objeto sobre la marcha. Los siguientes dos métodos son lo que puedo pensar. El segundo podría ajustarse a tus requisitos.

El Camino de KISS

El Keep It Simple Stupid manera

public class StorageObject { 
    public string Name { get; set; } 
    public string Birthday { get; set; } 
    public List<string> OtherInfo { get; set; } 
} 

Usted puede tener pares de nombre y valor que está separado por '|'

OtherInfo = {"Hobbies|Programming", "Website|Stackoverflow.com"} 

formas serializados

<StorageObject> 
    <Name>Matthew</Name> 
    <Birthday>Jan 1st, 2008</Birthday> 
    <OtherInfo> 
     <string>Hobbies|Programming</string> 
     <string>Website|Stackoverflow.com</string> 
    </OtherInfo> 
</StorageObject> 

La forma dinámica en C#

hacer la parte de nombre de par de valores se convierten en un elemento XML de modo que se puede construir de forma dinámica.

public class StorageObject { 
    public string Name { get; set; } 
    public string Birthday { get; set; } 
    public XElement OtherInfo { get; set; } // XmlElement for dot net 2 
} 

Usted puede construir fácilmente OtherInfo objeto como elemento de centrado por ejemplo,

XElement OtherInfo = new XElement("OtherInfo"); 
OtherInfo.Add(..Hobbies xelement & text value..); 
OtherInfo.Add(..WebSite xelement & text value..); 

La forma serializada será

<OtherInfo> 
    <Hobbies>Programming</Hobbies> 
    <Website>Stackoverflow.com</Website> 
</OtherInfo> 

o construirlo como atributo centrada

XElement OtherInfo = new XElement("OtherInfo"); 
OtherInfo.Add(..nvp xattribute Hobbies & value..); 
OtherInfo.Add(..nvp xattribute WebSite & value..); 

<OtherInfo> 
    <nvp n="Hobbies" v="Programming" /> 
    <nvp n="Website" v="Stackoverflow.com" /> 
</OtherInfo> 

Para cualquier lenguaje dinámico, se puede acceder a las propiedades directamente. Por lo demás, pueden acceder al valor leyendo el XML. Reading XML está bien soportado por la mayoría del framework.

+0

¡Gracias! Ambas son buenas ideas, Creo que puedo trabajar con cualquiera de esas soluciones para lograr mis objetivos. Buen trabajo en el XElement, no hubiera pensado en eso (falta de k ahora sobre eso. :PAG). –

+0

Sería más interesante una vez que comience a tratar con JSON para javascript. –

-1

No estoy seguro de que esto pueda resolver su problema (lo haría en C#, pero tal vez no en PHP), pero intente usar Dictionary<string,List<string>> OtherInfo en lugar de List<NameValuePairs>. Luego, "Hobbies" y "Sitios web" serían sus claves y los valores serían la lista de pasatiempos o sitios web. Aunque no estoy seguro de cómo se serializaría.

Usted sería capaz de hacer referencia a las listas de aficiones como:

List<string> hobbies = storageObject.OtherInfo["Hobbies"]; 

[editar] Véase here de un diccionario serializable XML genérico. Esta clase derivada es la que necesitaría usar en lugar del diccionario genérico.

+0

No se puede serializar un Dicationry a través de XML :(¡Gracias! –

+0

El diccionario es serializable si deriva de él y marca la clase derivada como serializable. Http://cs.rthand.com/blogs/blog_with_righthand/ archive/2005/08/06/How-to-deserialize-a-Dictionary_3C00_TKey_2C00_-TValue_3E00_-descenddant-using-binary-formatter.aspx – tvanfosson

+0

Sin embargo serializar el diccionario en WCF, el datacontractSerializer –

0

Eche un vistazo al atributo System.Xml.Serialization.XmlSerializerAssemblyAttribute. Esto le permite especificar un serializador de nivel de clase personalizado. Podrás escupir el XML que quieras.

Una forma rápida de ponerse al día sobre esto es usar sgen.exe para generar uno y echar un vistazo con Reflector.

-Oisin

1

Como una visión completamente diferente de esto, ¿por qué no pensar en hacerlo de manera completamente diferente. Tener un método de servicio web para devolver el objeto de almacenamiento serializado, menos el OtherInfo y otro método para devolver la lista de propiedades (claves) para OtherInfo, y un tercero para devolver la lista de valores para cualquier clave. De acuerdo, llevará más viajes de ida y vuelta al servicio web si quiere todos los datos, pero la solución será mucho más simple y más flexible.

[Serializable] 
public class StorageObject { 
    public string Name { get; set; } 
    public string Birthday { get; set; } 

    [Nonserializable] 
    public Dictionary<string,List<string>> OtherInfo { get; set; } 
} 

[WebMethod] 
public List<StorageObject> GetStorageObjects() { 
    // returns list of storage objects from persistent storage or cache 
} 

[WebMethod] 
public List<string> GetStorageObjectAttributes(string name) 
{ 
    // find storage object, sObj 
    return sObj.Keys.ToList(); 
} 

[WebMethod] 
public List<string> GetStorageObjectAtributeValues(sting name, string attribute) 
{ 
    // find storage object, sObj 
    return sObj[attribute]; 
} 
+0

¡Esa es una idea interesante! No estoy seguro de que sea una buena solución para lo que estoy trabajando en este momento, pero puedo ver que es otra forma de pensar y definitivamente es una solución. Gracias/tvanfosson /. –

4

Esto es lo que me he decidido.

Clase Estructura:

public class StorageObject { 
    public string Name { get; set; } 
    public string Birthday { get; set; } 
    [XmlAnyElement("Info")] // this prevents double-nodes in the XML 
    public XElement OtherInfo { get; set; } 
} 

Uso:

StorageObject o = new StorageObject(); 
o.OtherInfo.Add(new XElement("Hobbies","Programming"); 
o.OtherInfo.Add(new XElement("Website","Stackoverflow.com"); 

Salida:

<Info> 
    <Hobbies>Programming</Hobbies> 
    <Website>Stackoverflow.com</Website> 
</Info> 

me gustaría agradecer a todos por su ayuda, realmente aprecio la ayuda e ideas.

Cuestiones relacionadas