nano icon indicating copy to clipboard operation
nano copied to clipboard

Doubt about namespaces, nil values, etc

Open nobre84 opened this issue 11 years ago • 8 comments

Hi, I'm trying to consume an API, the soap envelope generated by Nano looks like this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
    <soapenv:Body>
        <CreateQueryStrings_Start>
            <userId>1234</userId>
            <password>abcd</password>
            <idOrgao>57</idOrgao>
            <numeroProcesso>1</numeroProcesso>
            <inputParameters>
                <ParameterList>
                    <Parameter>
                        <Name>TipoConsulta</Name>
                        <Value>Precatórios / Beneficiário</Value>
                    </Parameter>
                </ParameterList>
            </inputParameters>
        </CreateQueryStrings_Start>
    </soapenv:Body>
</soapenv:Envelope>

It returns incorrect results from the WCF webservice. The same input form is enveloped as this by a .NET MVC application:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <CreateQueryStrings_Start xmlns="http://tempuri.org/">
            <userId>1234</userId>
            <password>abcd</password>
            <idOrgao>57</idOrgao>
            <numeroProcesso>1</numeroProcesso>
            <inputParameters xmlns:a="schemas.datacloud.novaprolink.com.br/IWSConsultaProcesso" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
                <a:ParameterList>
                    <a:Parameter>
                        <a:Description i:nil="true"/>
                        <a:Name>TipoConsulta</a:Name>
                        <a:PossibleValues i:nil="true" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
                        <a:SuggestedValues i:nil="true" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
                        <a:Value>Precatórios / Beneficiário</a:Value>
                    </a:Parameter>
                </a:ParameterList>
            </inputParameters>
        </CreateQueryStrings_Start>
    </s:Body>
</s:Envelope>

What could be the reason it won't work ? Maybe the namespacing ? Nil value objects that aren't being encoded ? The wsdl is: http://wsrobos.novaprolink.com.br/v2/WSConsultaProcessos.WSConsultaProcesso.svc?wsdl

Regards nobre

nobre84 avatar Jul 05 '13 21:07 nobre84

Hi,

Nano is just a light library targeting Android platform, considering the complexity introduced, it only supports a single target namespace.

In your case, as workaround, I recommend you to do some hacking to the nano source, the code logic of nano is not complicated, hope you can make it:

  1. To support nested namespace, you may try to hack the source of XmlPullWriter.java https://github.com/bulldog2011/nano/blob/master/src/main/java/com/leansoft/nano/impl/XmlPullWriter.java you may try to update the function: protected void writeObject(XmlSerializer serializer, Object source, String namespace) throws Exception extract namespace from the object, compare it with the namespace passed in, if not equal, use the extracted namespace instead.
  2. To minimize xml message size, Nano does not serialize null field, but you can serialize null field as needed by hacking the source of XmlPullWriter.java

Let me know if you need additional help.

Thx! -William

bulldog2011 avatar Jul 06 '13 05:07 bulldog2011

Hi William,

could you elaborate on that a little more; I'm in the same situation trying to talk to a .net webservice, where nested namespaces seem to be used quite often.

I've looked into the XmlPullWriter.java, and have several questions:

  • How would you get the source object's namespace? First, I presume, I'd need to set the correct namespace annotation in the generated java source files, and then, in the writeObject(), somehow make use of that?
  • Would it be possible to alter the element names and prepend the namespace short aliases?

I'm trying to produce something along those lines:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tempuri="http://tempuri.org/"
xmlns:datacontract="http://schemas.datacontract.org/2004/07/SomeDataModel.Model">
   <soapenv:Body>
      <tempuri:GetPermissions>
         <tempuri:login>
            <datacontract:LoginName>user</datacontract:LoginName>
            <datacontract:Password>pass</datacontract:Password>
         </tempuri:login>
      </tempuri:GetPermissions>
   </soapenv:Body>
</soapenv:Envelope>

Thank you for any hints!

artjomsimon avatar Jul 18 '13 00:07 artjomsimon

Hi,

see my comments below:

  1. namespace is extracted from object using code like:
MappingSchema ms = MappingSchema.fromObject(source);
RootElementSchema res = ms.getRootElementSchema();
String namespace = res.getNamespace();
  1. namespace is auto-annotated on chasses during code generation, not all classes will have namespace annotations, it depends on the schema definition. it is not recommended that you annotate your class with namespace manually.
  2. It is possible to alter the element names and prepend the namespace short alias, however this is not recommended, I recommend you to do following to support nested namespace: in the source of XmlPullWriter.java https://github.com/bulldog2011/nano/blob/master/src/main/java/com/leansoft/nano/impl/XmlPullWriter.java you may try to update the function:
protected void writeObject(XmlSerializer serializer, Object source, String namespace) throws Exception

Extract namespace from the object, if not empty, compare it with the namespace passed in, if not equal, use the extracted namespace instead.

Thx! -William

bulldog2011 avatar Jul 18 '13 13:07 bulldog2011

I have added this in XmlPullWriter.writeObject method: MappingSchema ms = MappingSchema.fromObject(source); String innerNamespace = ms.getRootElementSchema().getNamespace(); if (innerNamespace != null && !innerNamespace.equals(namespace)) { namespace = innerNamespace; }

Is there any problem in this approach that would prevent this behavior to be added to the library ?

About the empty objects, I started doing a patch to the writeElement method to handle it , but it didn't work very well when adding attributes to the xml. And in fact it was pointless as the namespaces were enough to make it work as intended and with a smaller xml output.

nobre84 avatar Jul 18 '13 15:07 nobre84

Thx for your work and feedback, I plan to integrate the nested namespace logic into the latest source, will let you know when done.

-William

bulldog2011 avatar Jul 18 '13 16:07 bulldog2011

Thank you both! With your help, I finally managed to get my client to talk to a WCF web service.

The only difference from my WCF reference implementation is that the native client declares all the namespace in the head:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tempuri="http://tempuri.org/"
xmlns:datacontract="http://schemas.datacontract.org/2004/07/SomeDataModel.Model">
   <soapenv:Body>
      <tempuri:GetPermissions>
         <tempuri:login>
            <datacontract:LoginName>user</datacontract:LoginName>
            <datacontract:Password>pass</datacontract:Password>
         </tempuri:login>
      </tempuri:GetPermissions>
   </soapenv:Body>
</soapenv:Envelope>

whereas my solution using the modified XmlPullWriter (which isn't suited for this kind of refactoring, I presume) trivially writes the namespace on each element again:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://tempuri.org/">
   <soapenv:Body>
      <GetPermissions>
         <login>
            <n0:LoginName xmlns:n0="http://schemas.datacontract.org/2004/07/SomeDataModel.Model">user</n0:LoginName>
            <n1:Password xmlns:n1="http://schemas.datacontract.org/2004/07/SomeDataModel.Model">pass</n1:Password>
         </login>
      </GetPermissions>
   </soapenv:Body>
</soapenv:Envelope>

It works, but is rather verbose, especially if several children use the same namespace, which has an even higher impact on a mobile uplink.

I could mitigate this by using gzip (which is a good idea anyways), but that only works if I have control over the server's configuration.

Is this a limitation of the current nano/XmlPullParser implementation we're dealing with here, or am I doing something wrong?

Thanks for your support again!

artjomsimon avatar Jul 22 '13 22:07 artjomsimon

I ran into that issue as well, but I think in the current implementation it would be difficult to pre-declare the namespaces, unless one would traverse the hierarchy beforehand just to collect all the inner namespaces.

nobre84 avatar Jul 24 '13 14:07 nobre84

How to get off this n0, n1, n2 when you put namespaces?

<login>
            <n0:LoginName xmlns:n0="http://schemas.datacontract.org/2004/07/SomeDataModel.Model">user</n0:LoginName>
            <n1:Password xmlns:n1="http://schemas.datacontract.org/2004/07/SomeDataModel.Model">pass</n1:Password>
         </login>

marckaraujo avatar Aug 22 '13 12:08 marckaraujo