Por que não .NET desserializar o meu primitivo de matriz a partir de um web service?

StackOverflow https://stackoverflow.com/questions/30171

  •  09-06-2019
  •  | 
  •  

Pergunta

Ajuda!Eu tenho um Eixo de serviço da web que está sendo consumido por um aplicativo C#.Tudo funciona muito bem, exceto que as matrizes de valores longos sempre vir transversalmente como [0,0,0,0] - o direito de comprimento, mas os valores não são desserializado.Eu tentei com outros primitivos (ints, duplos) e acontece a mesma coisa.O que eu faço?Eu não quero alterar a semântica do meu serviço.

Foi útil?

Solução

Aqui está o que eu acabei com.Eu nunca encontrei outra solução para isso, então se você tiver algo melhor, por todos os meios, contribuir.

Primeiro, a longo matriz de definição wsdl:tipos de área:

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

Em seguida, vamos criar um SoapExtensionAttribute que irá executar a correção.Parece que o problema foi esse .NET não estava a seguir o multiref id para o elemento que contém o valor duplo.Então, nós processo a matriz do item, vá encontrar o valor, e, em seguida, inserir o valor para o 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, temos a marca de todos os métodos de Referência.cs arquivo que será desserializar um longo matriz com o nosso 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 correcção é longo específico, mas ele provavelmente poderia ser generalizado para lidar com qualquer tipo primitivo, tendo esse problema.

Outras dicas

Aqui está um mais ou menos colado cópia de uma versão post de blog Eu escrevi sobre o assunto.

Resumo executivo:Você pode alterar o caminho .NET desserializa o conjunto de resultados (ver Chris solução acima), ou você pode reconfigurar o Eixo para serializar os seus resultados de uma forma que é compatível com o .NET implementação de SOAP.

Se você for o último percurso, veja como:

...gerado classes de olhar e aparecer a função normalmente, mas se você vai olhar para o anular a serialização de matriz no cliente (.NET/WCF) lado você vai descobrir que o matriz foi desserializado incorretamente, e todos os valores do matriz são 0.Você terá que manualmente olha a resposta SOAP devolvidos por Eixo para descobrir o que está errado;aqui está um exemplo de resposta (novamente, editadas para maior clareza):

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

Você irá notar que o Eixo não gerar valores diretamente na voltou elemento, mas em vez disso, referências a elementos externos para valores.Isso pode fazer sentido quando há muitas referências a relativamente poucos valores discretos, mas qualquer que seja o caso, isso não é corretamente manipulados pelo WCF basicHttpBinding fornecedor (e supostamente por gSOAP e clássico .NET web de referências).

Levei um tempo para encontrar uma solução:editar o seu Eixo de implantação do server-config.wsdd arquivo e encontre o seguinte parâmetro:

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

Altere para false, reimplantar através da linha de comando, o que parece (no Windows) algo como esta:

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

O serviço de web a resposta deve agora ser deserializable pelo seu .NET cliente.

Encontrei este link que pode oferecer uma alternativa melhor: http://www.tomergabel.com/GettingWCFAndApacheAxisToBeFriendly.aspx

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top