Pregunta

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.Intentaré exponerlo lo mejor posible.

Objeto de almacenamiento de 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; }
}

Uso de muestra:

[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 y otras cosas:

Primero, lamento la extensión de la publicación, pero también quería brindar un código reproducible.

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

//ESTO ES IMPORTANTE

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

Si está en el otro formato, entonces no habría manera de lograrlo en absoluto.Además, en C#, si estoy consumiendo el servicio, también me gustaría poder hacer lo siguiente:

//ESTO ES IMPORTANTE

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

Lamentablemente, no estoy seguro de cómo lograrlo.Pude conseguirlo de esta manera, creando un diccionario personalizado que implementó IXmlSerializer (ver Desbordamiento de pila:Diccionario IXmlSerializer), sin embargo, hizo estallar el esquema WSDL fuera del agua.¡También es demasiado complicado y produjo resultados horribles en mi aplicación WinFormsTester!

Hay alguna forma de lograr esto ?¿Qué tipo de objetos necesito crear?¿Hay alguna forma de hacer esto que no sea creando una colección fuertemente tipada?Obviamente, si lo escribo fuertemente así:

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

Entonces funcionaría perfectamente, no tendría problemas con WSDL, podría acceder fácilmente desde PHP y C# (.OtherInfo.Hobbies).

Sin embargo, perdería por completo el sentido de las NVP, ya que tendría que saber de antemano cuál es la lista y no sería modificable.digamos, de una base de datos.

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

  1. El esquema WSDL no debería romperse
  2. Los pares de nombre-valor (NVP) deben serializarse en formato de atributo
  3. Debería ser fácil acceder a los NVP en PHP por su nombre ["Aficiones"]
  4. Debería ser de fácil acceso en C# (y ser compatible con su generador de Proxy)
  5. Ser fácilmente serializable
  6. No es necesario que escriba los datos con fuerza.

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

¿Fue útil?

Solución

Esto es como propiedades dinámicas para un objeto. C # no es un lenguaje dinámico a diferencia de JavaScript o quizás 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 sus requisitos.

El estilo KISS

La manera estúpida de mantenerlo simple

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

Puede tener pares de nombre y valor separados por '|'

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

Formularios 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 #

Haga que la parte del par nombre valor se convierta en un elemento XML para que pueda compilarlo dinámicamente.

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

Puede construir fácilmente el objeto OtherInfo como elemento centrado p.ej.

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

El formulario serializado será

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

o compilarlo como atributo centrado

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, puede acceder a las propiedades directamente. Por lo demás, pueden acceder al valor leyendo el XML. La lectura de XML está bien soportada por la mayoría del framework.

Otros consejos

Esto es lo que he decidido.

Estructura de clase:

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 y las ideas.

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 de cualquier clave.Por supuesto, será necesario realizar más viajes de ida y vuelta al servicio web si desea tener todos los datos, pero la solución será mucho más sencilla y 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];
}

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

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

-Oisina

No estoy seguro de que esto resuelva 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>. Entonces & Quot; Hobbies & Quot; y " Sitios web " serían sus claves y los valores serían la lista de pasatiempos o sitios web. Sin embargo, no estoy seguro de cómo se serializaría.

Podrías hacer referencia a las listas de pasatiempos como:

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

[EDITAR] Vea aquí para obtener una Diccionario serializable XML genérico. Esta clase derivada es la que necesitaría usar en lugar del Diccionario genérico.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top