Pregunta

¡Ayuda!Tengo un servicio web de Axis que está siendo consumido por una aplicación C#.Todo funciona muy bien, excepto que las matrices de valores largos siempre aparecen como [0,0,0,0], la longitud correcta, pero los valores no se deserializan.He probado con otras primitivas (ints, doubles) y pasa lo mismo.¿Qué debo hacer?No quiero cambiar la semántica de mi servicio.

¿Fue útil?

Solución

Esto es lo que terminé.Nunca he encontrado otra solución para esto, así que si tienes algo mejor, por supuesto, contribuye.

Primero, la definición de matriz larga en el área wsdl:types:

  <xsd:complexType name="ArrayOf_xsd_long">
    <xsd:complexContent mixed="false">
      <xsd:restriction base="soapenc:Array">
        <xsd:attribute wsdl:arrayType="soapenc:long[]" ref="soapenc:arrayType" />
      </xsd:restriction>
    </xsd:complexContent>
  </xsd:complexType>

A continuación, creamos un SoapExtensionAttribute que realizará la corrección.Parece que el problema era que .NET no seguía la identificación múltiple del elemento que contenía el valor doble.Entonces, procesamos el elemento de la matriz, buscamos el valor y luego lo insertamos en el elemento:

[AttributeUsage(AttributeTargets.Method)]
public class LongArrayHelperAttribute : SoapExtensionAttribute
{
    private int priority = 0;

    public override Type ExtensionType
    {
        get { return typeof (LongArrayHelper); }
    }

    public override int Priority
    {
        get { return priority; }
        set { priority = value; }
    }
}

public class LongArrayHelper : SoapExtension
{
    private static ILog log = LogManager.GetLogger(typeof (LongArrayHelper));

    public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
    {
        return null;
    }

    public override object GetInitializer(Type serviceType)
    {
        return null;
    }

    public override void Initialize(object initializer)
    {
    }

    private Stream originalStream;

    private Stream newStream;

    public override void ProcessMessage(SoapMessage m)
    {
        switch (m.Stage)
        {
            case SoapMessageStage.AfterSerialize:
                newStream.Position = 0; //need to reset stream 
                CopyStream(newStream, originalStream);
                break;

            case SoapMessageStage.BeforeDeserialize:
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Indent = false;
                settings.NewLineOnAttributes = false;
                settings.NewLineHandling = NewLineHandling.None;
                settings.NewLineChars = "";
                XmlWriter writer = XmlWriter.Create(newStream, settings);

                XmlDocument xmlDocument = new XmlDocument();
                xmlDocument.Load(originalStream);

                List<XmlElement> longArrayItems = new List<XmlElement>();
                Dictionary<string, XmlElement> multiRefs = new Dictionary<string, XmlElement>();
                FindImportantNodes(xmlDocument.DocumentElement, longArrayItems, multiRefs);
                FixLongArrays(longArrayItems, multiRefs);

                xmlDocument.Save(writer);
                newStream.Position = 0;
                break;
        }
    }

    private static void FindImportantNodes(XmlElement element, List<XmlElement> longArrayItems,
                                           Dictionary<string, XmlElement> multiRefs)
    {
        string val = element.GetAttribute("soapenc:arrayType");
        if (val != null && val.Contains(":long["))
        {
            longArrayItems.Add(element);
        }
        if (element.Name == "multiRef")
        {
            multiRefs[element.GetAttribute("id")] = element;
        }
        foreach (XmlNode node in element.ChildNodes)
        {
            XmlElement child = node as XmlElement;
            if (child != null)
            {
                FindImportantNodes(child, longArrayItems, multiRefs);
            }
        }
    }

    private static void FixLongArrays(List<XmlElement> longArrayItems, Dictionary<string, XmlElement> multiRefs)
    {
        foreach (XmlElement element in longArrayItems)
        {
            foreach (XmlNode node in element.ChildNodes)
            {
                XmlElement child = node as XmlElement;
                if (child != null)
                {
                    string href = child.GetAttribute("href");
                    if (href == null || href.Length == 0)
                    {
                        continue;
                    }
                    if (href.StartsWith("#"))
                    {
                        href = href.Remove(0, 1);
                    }
                    XmlElement multiRef = multiRefs[href];
                    if (multiRef == null)
                    {
                        continue;
                    }
                    child.RemoveAttribute("href");
                    child.InnerXml = multiRef.InnerXml;
                    if (log.IsDebugEnabled)
                    {
                        log.Debug("Replaced multiRef id '" + href + "' with value: " + multiRef.InnerXml);
                    }
                }
            }
        }
    }

    public override Stream ChainStream(Stream s)
    {
        originalStream = s;
        newStream = new MemoryStream();
        return newStream;
    }

    private static void CopyStream(Stream from, Stream to)
    {
        TextReader reader = new StreamReader(from);
        TextWriter writer = new StreamWriter(to);
        writer.WriteLine(reader.ReadToEnd());
        writer.Flush();
    }
}

Finalmente, etiquetamos todos los métodos en el archivo Reference.cs que deserializarán una matriz larga con nuestro atributo:

    [SoapRpcMethod("", RequestNamespace="http://some.service.provider",
        ResponseNamespace="http://some.service.provider")]
    [return : SoapElement("getFooReturn")]
    [LongArrayHelper]
    public Foo getFoo()
    {
        object[] results = Invoke("getFoo", new object[0]);
        return ((Foo) (results[0]));
    }

Esta solución es específica durante mucho tiempo, pero probablemente podría generalizarse para manejar cualquier tipo primitivo que tenga este problema.

Otros consejos

Aquí hay una versión más o menos copiada y pegada de un entrada en el blog Escribí sobre el tema.

Resumen ejecutivo:Puede cambiar la forma en que .NET deserializa el conjunto de resultados (consulte la solución de Chris arriba) o puede reconfigurar Axis para serializar sus resultados de una manera que sea compatible con la implementación de .NET SOAP.

Si sigue la última ruta, así es como:

...Las clases generadas se ven y parecen funcionar normalmente, pero si observa la matriz deserializada en el lado del cliente (.NET/WCF), encontrará que la matriz se ha deserializado incorrectamente, y todos los valores en la matriz son 0 .Tendrá que mirar manualmente la respuesta de jabón devuelta por el eje para descubrir qué está mal;Aquí hay una respuesta de muestra (nuevamente, editada para mayor claridad):

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/>
    <soapenv:Body>
        <doSomethingResponse>
          <doSomethingReturn>
            <doSomethingReturn href="#id0"/>
            <doSomethingReturn href="#id1"/>
            <doSomethingReturn href="#id2"/>
            <doSomethingReturn href="#id3"/>
            <doSomethingReturn href="#id4"/>
          </doSomethingReturn>
        </doSomethingResponse>
        <multiRef id="id4">5</multiRef>
        <multiRef id="id3">4</multiRef>
        <multiRef id="id2">3</multiRef>
        <multiRef id="id1">2</multiRef>
        <multiRef id="id0">1</multiRef>
   </soapenv:Body>
</soapenv:Envelope>

Notará que el eje no genera valores directamente en el elemento devuelto, sino que hace referencia a elementos externos para valores.Esto podría tener sentido cuando hay muchas referencias a relativamente pocos valores discretos, pero cualquiera que sea el caso que esto no sea manejado correctamente por el proveedor WCF Basichttpbinding (y, según los informes, también por GSOAP y las referencias web clásicas de .NET).

Me tomó un tiempo encontrar una solución:Edite el archivo Server-Config.wsdd de su implementación de eje y busque el siguiente parámetro:

<parameter name="sendMultiRefs" value="true"/>

Cámbielo a False, luego vuelva a desplegar a través de la línea de comando, que se ve (en Windows) algo como esto:

java -cp %AXISCLASSPATH% org.apache.axis.client.AdminClient server-config.wsdl 

La respuesta del servicio web ahora debe ser deserializable por su cliente .NET.

Encontré este enlace que puede ofrecer una mejor alternativa: http://www.tomergabel.com/GettingWCFAndApacheAxisToBeFriendly.aspx

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