Frage

Helfen!Ich habe einen Axis-Webdienst, der von einer C#-Anwendung genutzt wird.Alles funktioniert großartig, außer dass Arrays mit langen Werten immer als [0,0,0,0] angezeigt werden – die richtige Länge, aber die Werte werden nicht deserialisiert.Ich habe es mit anderen Grundelementen (Ints, Doubles) versucht und das Gleiche passiert.Was mache ich?Ich möchte die Semantik meines Dienstes nicht ändern.

War es hilfreich?

Lösung

Hier ist, was ich am Ende herausgefunden habe.Ich habe dafür noch nie eine andere Lösung gefunden. Wenn Sie also etwas Besseres haben, tragen Sie auf jeden Fall einen Beitrag bei.

Zuerst die lange Array-Definition im Bereich 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>

Als Nächstes erstellen wir ein SoapExtensionAttribute, das die Korrektur durchführt.Es scheint, dass das Problem darin bestand, dass .NET der Multiref-ID nicht zu dem Element folgte, das den Double-Wert enthielt.Also verarbeiten wir das Array-Element, suchen den Wert und fügen ihn dann in das Element ein:

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

Schließlich markieren wir alle Methoden in der Datei Reference.cs, die ein langes Array deserialisieren, mit unserem 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]));
    }

Dieser Fix ist long-spezifisch, könnte aber wahrscheinlich verallgemeinert werden, um jeden primitiven Typ zu behandeln, bei dem dieses Problem auftritt.

Andere Tipps

Hier ist eine mehr oder weniger kopierte Version von a Blogeintrag Ich habe zu diesem Thema geschrieben.

Zusammenfassung:Sie können entweder die Art und Weise ändern, wie .NET die Ergebnismenge deserialisiert (siehe Chris' Lösung oben), oder Sie können Axis neu konfigurieren, um seine Ergebnisse auf eine Weise zu serialisieren, die mit der .NET SOAP-Implementierung kompatibel ist.

Wenn Sie den letzteren Weg wählen, gehen Sie wie folgt vor:

...Die generierten Klassen scheinen normal zu funktionieren. Wenn Sie sich jedoch das von Deserialized Array auf der Seite Client (.NET/WCF) ansehen, werden Sie feststellen, dass das Array falsch deserialisiert wurde und alle Werte im Array 0 sind .Sie müssen sich die von der Achse zurückgegebene Seifenreaktion manuell ansehen, um herauszufinden, was los ist.Hier ist eine Beispielantwort (erneut, bearbeitet für Klarheit):

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

Sie werden feststellen, dass Axis nicht direkt im zurückgegebenen Element Werte erzeugt, sondern auf externe Elemente für Werte.Dies mag sinnvoll sein, wenn es viele Hinweise auf relativ wenige diskrete Werte gibt, aber wie auch immer dies der Fall ist, wird nicht ordnungsgemäß vom WCF Basischtttpbinding -Anbieter behandelt (und Berichten zufolge auch von GSOAP und klassischen .NET -Webreferenzen).

Es hat eine Weile gedauert, bis ich eine Lösung gefunden habe:Bearbeiten Sie die Server-Config.WSDD-Datei der Axis-Bereitstellung und finden Sie den folgenden Parameter:

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

Ändern Sie es in False, dann über die Befehlszeile, die (unter Windows) so etwas aussieht:

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

Die Antwort des Webdienstes sollte nun von Ihrem .NET -Client deserialisierbar sein.

Ich habe diesen Link gefunden, der möglicherweise eine bessere Alternative bietet: http://www.tomergabel.com/GettingWCFAndApacheApacheAxisToBeFriendly.aspx

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top