.NET为什么不从Web服务反序列化我的原始数组?

时间:2020-03-05 18:43:51  来源:igfitidea点击:

帮助!我有一个C应用程序正在使用的Axis Web服务。一切工作都很好,除了长值数组始终以正确的长度作为[0,0,0,0]遇到,但不会反序列化值。我尝试了其他原语(整数,双精度),并且发生了相同的事情。我该怎么办?我不想更改服务的语义。

解决方案

回答

这就是我最后得到的。我还没有找到其他解决方案,因此,如果我们有更好的选择,请务必提供帮助。

首先,在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>

接下来,我们创建一个SoapExtensionAttribute来执行修复。似乎问题在于,.NET没有在包含double值的元素上遵循multiref id。因此,我们处理数组项,找到值,然后将值插入到元素中:

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

最后,我们在Reference.cs文件中标记将使用我们的属性反序列化长数组的所有方法:

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

此修复程序是长期特定的,但可能可以推广为解决任何存在此问题的原始类型。

回答

找到了可以提供更好替代方法的链接:http://www.tomergabel.com/GettingWCFAndApacheAxisToBeFriendly.aspx

回答

这是我撰写的有关该主题的博客文章的或者多或者少复制粘贴的版本。

摘要:我们可以更改.NET对结果集进行反序列化的方式(请参见上面的Chris解决方案),也可以重新配置Axis以与.NET SOAP实现兼容的方式序列化其结果。

如果我们走后一种路线,请按以下步骤操作:

... the generated
  classes look and appear to function
  normally, but if you'll look at the
  deserialized array on the client
  (.NET/WCF) side you'll find that the
  array has been deserialized
  incorrectly, and all values in the
  array are 0. You'll have to manually
  look at the SOAP response returned by
  Axis to figure out what's wrong;
  here's a sample response (again,
  edited for clarity):
<?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>
You'll notice that Axis does not
  generate values directly in the
  returned element, but instead
  references external elements for
  values. This might make sense when
  there are many references to
  relatively few discrete values, but
  whatever the case this is not properly
  handled by the WCF basicHttpBinding
  provider (and reportedly by gSOAP and
  classic .NET web references as well).
  
  It took me a while to find a solution:
  edit your Axis deployment's
  server-config.wsdd file and find the
  following parameter:
<parameter name="sendMultiRefs" value="true"/>
Change it to false,
  then redeploy via the command line,
  which looks (under Windows) something
  like this:
java -cp %AXISCLASSPATH% org.apache.axis.client.AdminClient server-config.wsdl
The web service's
  response should now be deserializable
  by your .NET client.