Pourquoi .NET ne désérialise-t-il pas mon tableau primitif à partir d'un service Web ?

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

  •  09-06-2019
  •  | 
  •  

Question

Aide!J'ai un service Web Axis qui est utilisé par une application C#.Tout fonctionne très bien, sauf que les tableaux de valeurs longues apparaissent toujours comme [0,0,0,0] - la bonne longueur, mais les valeurs ne sont pas désérialisées.J'ai essayé avec d'autres primitives (entiers, doubles) et la même chose se produit.Que dois-je faire?Je ne veux pas changer la sémantique de mon service.

Était-ce utile?

La solution

Voici ce avec quoi j'ai fini.Je n'ai jamais trouvé d'autre solution pour cela, donc si vous avez quelque chose de mieux, n'hésitez pas à contribuer.

Tout d’abord, la longue définition du tableau dans la zone 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>

Ensuite, nous créons un SoapExtensionAttribute qui effectuera le correctif.Il semble que le problème était que .NET ne suivait pas l'identifiant multiref jusqu'à l'élément contenant la valeur double.Nous traitons donc l'élément du tableau, cherchons la valeur, puis insérons-la dans l'élément :

[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();
    }
}

Enfin, nous marquons toutes les méthodes du fichier Reference.cs qui désérialiseront un long tableau avec notre attribut :

    [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]));
    }

Ce correctif est spécifique à la longue période, mais il pourrait probablement être généralisé pour gérer tout type primitif rencontrant ce problème.

Autres conseils

Voici une version plus ou moins copiée-collée d'un article de blog J'ai écrit sur le sujet.

Résumé exécutif:Vous pouvez soit modifier la façon dont .NET désérialise le jeu de résultats (voir la solution de Chris ci-dessus), soit reconfigurer Axis pour sérialiser ses résultats d'une manière compatible avec l'implémentation .NET SOAP.

Si vous optez pour cette dernière voie, voici comment procéder :

...Les classes générées semblent et semblent fonctionner normalement, mais si vous regardez le tableau désérialisé du côté client (.NET / WCF), vous constaterez que le tableau a été incorrectement désérialisé, et toutes les valeurs du tableau sont 0 sont 0 .Vous devrez regarder manuellement la réponse SOAP renvoyée par Axis pour comprendre ce qui ne va pas;Voici un exemple de réponse (encore une fois, édité pour plus de clarté):

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

Vous remarquerez que Axis ne génère pas de valeurs directement dans l'élément renvoyé, mais fait plutôt référence aux éléments externes pour les valeurs.Cela peut avoir un sens lorsqu'il existe de nombreuses références à relativement peu de valeurs discrètes, mais quel que soit le cas, ce n'est pas correctement géré par le fournisseur WCF Basichttpbinding (et aurait également été référentiel GSOAP et .NET .NET .NET).

Il m'a fallu du temps pour trouver une solution :Modifiez le fichier Server-Config.wsdd de votre déploiement AXIS et recherchez le paramètre suivant:

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

Changez-le en faux, puis redéposez via la ligne de commande, qui ressemble (sous Windows) quelque chose comme ceci:

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

La réponse du service Web doit désormais être désérialisable par votre client .NET.

J'ai trouvé ce lien qui peut offrir une meilleure alternative : http://www.tomergabel.com/GettingWCFAndApacheAxisToBeFriendly.aspx

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top