willemdj / erlsom

XML parser for Erlang
GNU Lesser General Public License v3.0
265 stars 103 forks source link

erlsom_write cannot create XML document #72

Closed Koziolek closed 5 years ago

Koziolek commented 5 years ago

Hi, I try to create SOAP client with this hrl file:

-record(faultdetail, {uri :: string(),
                      tag :: string(),
                      text :: string()}).

-record(faultcode, {uri :: string(),
                    code :: string(),
                    subcode :: #faultcode{} % only v. 1.2
                   }).

-record(faultreason, {text :: string(),
                      language :: string()}).

-record(soap_fault_1_1, {faultcode :: #faultcode{},
                         faultstring :: string(),
                         faultactor :: string(),
                         detail :: [#faultdetail{}]}).

-record(soap_fault_1_2, {code :: #faultcode{},
                         reason :: [#faultreason{}],
                         role :: string(),
                         detail :: [#faultdetail{}]}).

%% xsd:QName values are translated to #qname{} records.
-record(qname, {uri :: string(),
                localPart :: string(),
                prefix :: string(),
                mappedPrefix :: string()}).

-record('P0:Pair', {
    'Name' :: string(),
    'Value' :: string()}).

-type 'P0:Pair'() :: #'P0:Pair'{}.

-record('P0:RowData', {
    'RowData' :: ['P0:Pair'()]}).

-type 'P0:RowData'() :: #'P0:RowData'{}.

-record('P0:ColumnNames', {
    'Column' :: [string()]}).

-type 'P0:ColumnNames'() :: #'P0:ColumnNames'{}.

-record('P0:DataSet', {
    'Columns' :: 'P0:ColumnNames'(),
    'Row' :: ['P0:RowData'()] | undefined}).

-type 'P0:DataSet'() :: #'P0:DataSet'{}.

-record('P0:getBIDataResponse', {
    'Message' :: string(),
    'Data' :: 'P0:DataSet'() | undefined}).

-type 'P0:getBIDataResponse'() :: #'P0:getBIDataResponse'{}.

-record('P0:getBIData', {
    'MetricName' :: string(),
    'ClientID' :: string(),
    'Username' :: string(),
    'Password' :: string(),
    'FromDate' :: string() | undefined,
    'ToDate' :: string() | undefined,
    'Parameters' :: [string()] | undefined}).

-type 'P0:getBIData'() :: #'P0:getBIData'{}.
-define(INTERFACE, {interface,"BIData",'BIData','1.1',soap_client_inets,
                    soap_server_cowboy_1,undefined,'BIData_wsdl_client',[],
                    "https://ws.jobdiva.com/BIData/",
                    "http://schemas.xmlsoap.org/soap/envelope/","document",
                    undefined,"https://ws.jobdiva.com/axis2/services/BIData/",
                    "BIDataSOAP","BIDataSOAP","BIData",
                    [{op,"getBIData",getBIData,
                      "https://ws.jobdiva.com/BIData/getBIData",undefined,
                      request_response,'P0:getBIData','P0:getBIDataResponse',
                      undefined}],
                    {model,
                     [{type,'_document',sequence,
                       [{el,
                         [{alt,'P0:getBIData','P0:getBIData',[],1,1,true,
                           undefined},
                          {alt,'P0:getBIDataResponse','P0:getBIDataResponse',
                           [],1,1,true,undefined}],
                         1,1,undefined,2}],
                       [],undefined,undefined,1,1,1,false,undefined},
                      {type,'P0:Pair',sequence,
                       [{el,
                         [{alt,'Name',{'#PCDATA',char},[],1,1,true,undefined}],
                         1,1,undefined,2},
                        {el,
                         [{alt,'Value',
                           {'#PCDATA',char},
                           [],1,1,true,undefined}],
                         1,1,undefined,3}],
                       [],undefined,undefined,3,1,1,undefined,undefined},
                      {type,'P0:RowData',sequence,
                       [{el,
                         [{alt,'RowData','P0:Pair',[],1,1,true,undefined}],
                         1,unbound,undefined,2}],
                       [],undefined,undefined,2,1,1,undefined,undefined},
                      {type,'P0:ColumnNames',sequence,
                       [{el,
                         [{alt,'Column',
                           {'#PCDATA',char},
                           [],1,1,true,undefined}],
                         1,unbound,undefined,2}],
                       [],undefined,undefined,2,1,1,undefined,undefined},
                      {type,'P0:DataSet',sequence,
                       [{el,
                         [{alt,'Columns','P0:ColumnNames',[],1,1,true,
                           undefined}],
                         1,1,undefined,2},
                        {el,
                         [{alt,'Row','P0:RowData',[],1,1,true,undefined}],
                         0,unbound,undefined,3}],
                       [],undefined,undefined,3,1,1,undefined,undefined},
                      {type,'P0:getBIDataResponse',sequence,
                       [{el,
                         [{alt,'Message',
                           {'#PCDATA',char},
                           [],1,1,true,undefined}],
                         1,1,undefined,2},
                        {el,
                         [{alt,'Data','P0:DataSet',[],1,1,true,undefined}],
                         0,1,undefined,3}],
                       [],undefined,undefined,3,1,1,undefined,undefined},
                      {type,'P0:getBIData',sequence,
                       [{el,
                         [{alt,'MetricName',
                           {'#PCDATA',char},
                           [],1,1,true,undefined}],
                         1,1,undefined,2},
                        {el,
                         [{alt,'ClientID',
                           {'#PCDATA',char},
                           [],1,1,true,undefined}],
                         1,1,undefined,3},
                        {el,
                         [{alt,'Username',
                           {'#PCDATA',char},
                           [],1,1,true,undefined}],
                         1,1,undefined,4},
                        {el,
                         [{alt,'Password',
                           {'#PCDATA',char},
                           [],1,1,true,undefined}],
                         1,1,undefined,5},
                        {el,
                         [{alt,'FromDate',
                           {'#PCDATA',char},
                           [],1,1,true,undefined}],
                         0,1,undefined,6},
                        {el,
                         [{alt,'ToDate',
                           {'#PCDATA',char},
                           [],1,1,true,undefined}],
                         0,1,undefined,7},
                        {el,
                         [{alt,'Parameters',
                           {'#PCDATA',char},
                           [],1,1,true,undefined}],
                         0,unbound,undefined,8}],
                       [],undefined,undefined,8,1,1,undefined,undefined}],
                     [{ns,"https://ws.jobdiva.com/BIData/","P0",unqualified},
                      {ns,"http://www.w3.org/2001/XMLSchema","xsd",qualified}],
                     "https://ws.jobdiva.com/BIData/",[],false,skip},
                    1,undefined,
                    [{"https://ws.jobdiva.com/BIData/","P0"}]}).

I use bet365/soap that call erlsom_write.write function. That function throw error. I debbug code and I found that call of function findAlternative/3 with params:

< RecordType = <<"P0:getBIData">>

< Alternatives = [{alt,'P0:getBIData','P0:getBIData',[],1,1,true,undefined},
                  {alt,'P0:getBIDataResponse','P0:getBIDataResponse',[],1,1,
                       true,undefined}]
< Model = {model,[{type,'_document',sequence,
                        [{el,[{alt,'P0:getBIData','P0:getBIData',[],1,1,true,
                                   undefined},
                              {alt,'P0:getBIDataResponse',
                                   'P0:getBIDataResponse',[],1,1,true,
                                   undefined}],
                             1,1,undefined,2}],
                        [],undefined,undefined,1,1,1,false,undefined},
                  {type,'P0:Pair',sequence,
                        [{el,[{alt,'Name',
                                   {'#PCDATA',char},
                                   [],1,1,true,undefined}],
                             1,1,undefined,2},
                         {el,[{alt,'Value',
                                   {'#PCDATA',char},
                                   [],1,1,true,undefined}],
                             1,1,undefined,3}],
                        [],undefined,undefined,3,1,1,undefined,undefined},
                  {type,'P0:RowData',sequence,
                        [{el,[{alt,'RowData','P0:Pair',[],1,1,true,undefined}],
                             1,unbound,undefined,2}],
                        [],undefined,undefined,2,1,1,undefined,undefined},
                  {type,'P0:ColumnNames',sequence,
                        [{el,[{alt,'Column',
                                   {'#PCDATA',char},
                                   [],1,1,true,undefined}],
                             1,unbound,undefined,2}],
                        [],undefined,undefined,2,1,1,undefined,undefined},
                  {type,'P0:DataSet',sequence,
                        [{el,[{alt,'Columns','P0:ColumnNames',[],1,1,true,
                                   undefined}],
                             1,1,undefined,2},
                         {el,[{alt,'Row','P0:RowData',[],1,1,true,undefined}],
                             0,unbound,undefined,3}],
                        [],undefined,undefined,3,1,1,undefined,undefined},
                  {type,'P0:getBIDataResponse',sequence,
                        [{el,[{alt,'Message',
                                   {'#PCDATA',char},
                                   [],1,1,true,undefined}],
                             1,1,undefined,2},
                         {el,[{alt,'Data','P0:DataSet',[],1,1,true,undefined}],
                             0,1,undefined,3}],
                        [],undefined,undefined,3,1,1,undefined,undefined},
                  {type,'P0:getBIData',sequence,
                        [{el,[{alt,'MetricName',
                                   {'#PCDATA',char},
                                   [],1,1,true,undefined}],
                             1,1,undefined,2},
                         {el,[{alt,'ClientID',
                                   {'#PCDATA',char},
                                   [],1,1,true,undefined}],
                             1,1,undefined,3},
                         {el,[{alt,'Username',
                                   {'#PCDATA',char},
                                   [],1,1,true,undefined}],
                             1,1,undefined,4},
                         {el,[{alt,'Password',
                                   {'#PCDATA',char},
                                   [],1,1,true,undefined}],
                             1,1,undefined,5},
                         {el,[{alt,'FromDate',
                                   {'#PCDATA',char},
                                   [],1,1,true,undefined}],
                             0,1,undefined,6},
                         {el,[{alt,'ToDate',
                                   {'#PCDATA',char},
                                   [],1,1,true,undefined}],
                             0,1,undefined,7},
                         {el,[{alt,'Parameters',
                                   {'#PCDATA',char},
                                   [],1,1,true,undefined}],
                             0,unbound,undefined,8}],
                        [],undefined,undefined,8,1,1,undefined,undefined}],
                 [{ns,"http://ws.jobdiva.com/BIData/","P0",unqualified},
                  {ns,"http://www.w3.org/2001/XMLSchema","xsd",qualified}],
                 "http://ws.jobdiva.com/BIData/",[],false,skip}

exit with error with message: Struct doesn't match model: recordtype not expected: P0:getBIData

That provides to error on soap client and finally to error in my app.

Problem

It's look like erlsom_write:findAlternative/3 try to find parent elemnt of root element in XML.

willemdj commented 5 years ago

Hi,

Could you also share the WSDL? And the exact way how you are trying to use the soap client (the function call in your code that is causing the error to occur)?

Regards, Willem

Op wo 12 jun. 2019 om 15:22 schreef Koziołek notifications@github.com:

Hi, I try to create SOAP client with this hrl file:

-record(faultdetail, {uri :: string(), tag :: string(), text :: string()}).

-record(faultcode, {uri :: string(), code :: string(), subcode :: #faultcode{} % only v. 1.2 }).

-record(faultreason, {text :: string(), language :: string()}).

-record(soap_fault_1_1, {faultcode :: #faultcode{}, faultstring :: string(), faultactor :: string(), detail :: [#faultdetail{}]}).

-record(soap_fault_1_2, {code :: #faultcode{}, reason :: [#faultreason{}], role :: string(), detail :: [#faultdetail{}]}). %% xsd:QName values are translated to #qname{} records. -record(qname, {uri :: string(), localPart :: string(), prefix :: string(), mappedPrefix :: string()}).

-record('P0:Pair', { 'Name' :: string(), 'Value' :: string()}).

-type 'P0:Pair'() :: #'P0:Pair'{}.

-record('P0:RowData', { 'RowData' :: ['P0:Pair'()]}).

-type 'P0:RowData'() :: #'P0:RowData'{}.

-record('P0:ColumnNames', { 'Column' :: [string()]}).

-type 'P0:ColumnNames'() :: #'P0:ColumnNames'{}.

-record('P0:DataSet', { 'Columns' :: 'P0:ColumnNames'(), 'Row' :: ['P0:RowData'()] | undefined}).

-type 'P0:DataSet'() :: #'P0:DataSet'{}.

-record('P0:getBIDataResponse', { 'Message' :: string(), 'Data' :: 'P0:DataSet'() | undefined}).

-type 'P0:getBIDataResponse'() :: #'P0:getBIDataResponse'{}.

-record('P0:getBIData', { 'MetricName' :: string(), 'ClientID' :: string(), 'Username' :: string(), 'Password' :: string(), 'FromDate' :: string() | undefined, 'ToDate' :: string() | undefined, 'Parameters' :: [string()] | undefined}).

-type 'P0:getBIData'() :: #'P0:getBIData'{}. -define(INTERFACE, {interface,"BIData",'BIData','1.1',soap_client_inets, soap_server_cowboy_1,undefined,'BIData_wsdl_client',[], "https://ws.jobdiva.com/BIData/", "http://schemas.xmlsoap.org/soap/envelope/","document", undefined,"https://ws.jobdiva.com/axis2/services/BIData/", "BIDataSOAP","BIDataSOAP","BIData", [{op,"getBIData",getBIData, "https://ws.jobdiva.com/BIData/getBIData",undefined, request_response,'P0:getBIData','P0:getBIDataResponse', undefined}], {model, [{type,'_document',sequence, [{el, [{alt,'P0:getBIData','P0:getBIData',[],1,1,true, undefined}, {alt,'P0:getBIDataResponse','P0:getBIDataResponse', [],1,1,true,undefined}], 1,1,undefined,2}], [],undefined,undefined,1,1,1,false,undefined}, {type,'P0:Pair',sequence, [{el, [{alt,'Name',{'#PCDATA',char},[],1,1,true,undefined}], 1,1,undefined,2}, {el, [{alt,'Value', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,3}], [],undefined,undefined,3,1,1,undefined,undefined}, {type,'P0:RowData',sequence, [{el, [{alt,'RowData','P0:Pair',[],1,1,true,undefined}], 1,unbound,undefined,2}], [],undefined,undefined,2,1,1,undefined,undefined}, {type,'P0:ColumnNames',sequence, [{el, [{alt,'Column', {'#PCDATA',char}, [],1,1,true,undefined}], 1,unbound,undefined,2}], [],undefined,undefined,2,1,1,undefined,undefined}, {type,'P0:DataSet',sequence, [{el, [{alt,'Columns','P0:ColumnNames',[],1,1,true, undefined}], 1,1,undefined,2}, {el, [{alt,'Row','P0:RowData',[],1,1,true,undefined}], 0,unbound,undefined,3}], [],undefined,undefined,3,1,1,undefined,undefined}, {type,'P0:getBIDataResponse',sequence, [{el, [{alt,'Message', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,2}, {el, [{alt,'Data','P0:DataSet',[],1,1,true,undefined}], 0,1,undefined,3}], [],undefined,undefined,3,1,1,undefined,undefined}, {type,'P0:getBIData',sequence, [{el, [{alt,'MetricName', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,2}, {el, [{alt,'ClientID', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,3}, {el, [{alt,'Username', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,4}, {el, [{alt,'Password', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,5}, {el, [{alt,'FromDate', {'#PCDATA',char}, [],1,1,true,undefined}], 0,1,undefined,6}, {el, [{alt,'ToDate', {'#PCDATA',char}, [],1,1,true,undefined}], 0,1,undefined,7}, {el, [{alt,'Parameters', {'#PCDATA',char}, [],1,1,true,undefined}], 0,unbound,undefined,8}], [],undefined,undefined,8,1,1,undefined,undefined}], [{ns,"https://ws.jobdiva.com/BIData/","P0",unqualified}, {ns,"http://www.w3.org/2001/XMLSchema","xsd",qualified}], "https://ws.jobdiva.com/BIData/",[],false,skip}, 1,undefined, [{"https://ws.jobdiva.com/BIData/","P0"}]}).

I use bet365/soap that call erlsom_write.write function. That function throw error. I debbug code and I found that call of function findAlternative/3 with params:

< RecordType = <<"P0:getBIData">>

< Alternatives = [{alt,'P0:getBIData','P0:getBIData',[],1,1,true,undefined}, {alt,'P0:getBIDataResponse','P0:getBIDataResponse',[],1,1, true,undefined}]

< Model = {model,[{type,'_document',sequence, [{el,[{alt,'P0:getBIData','P0:getBIData',[],1,1,true, undefined}, {alt,'P0:getBIDataResponse', 'P0:getBIDataResponse',[],1,1,true, undefined}], 1,1,undefined,2}], [],undefined,undefined,1,1,1,false,undefined}, {type,'P0:Pair',sequence, [{el,[{alt,'Name', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,2}, {el,[{alt,'Value', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,3}], [],undefined,undefined,3,1,1,undefined,undefined}, {type,'P0:RowData',sequence, [{el,[{alt,'RowData','P0:Pair',[],1,1,true,undefined}], 1,unbound,undefined,2}], [],undefined,undefined,2,1,1,undefined,undefined}, {type,'P0:ColumnNames',sequence, [{el,[{alt,'Column', {'#PCDATA',char}, [],1,1,true,undefined}], 1,unbound,undefined,2}], [],undefined,undefined,2,1,1,undefined,undefined}, {type,'P0:DataSet',sequence, [{el,[{alt,'Columns','P0:ColumnNames',[],1,1,true, undefined}], 1,1,undefined,2}, {el,[{alt,'Row','P0:RowData',[],1,1,true,undefined}], 0,unbound,undefined,3}], [],undefined,undefined,3,1,1,undefined,undefined}, {type,'P0:getBIDataResponse',sequence, [{el,[{alt,'Message', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,2}, {el,[{alt,'Data','P0:DataSet',[],1,1,true,undefined}], 0,1,undefined,3}], [],undefined,undefined,3,1,1,undefined,undefined}, {type,'P0:getBIData',sequence, [{el,[{alt,'MetricName', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,2}, {el,[{alt,'ClientID', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,3}, {el,[{alt,'Username', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,4}, {el,[{alt,'Password', {'#PCDATA',char}, [],1,1,true,undefined}], 1,1,undefined,5}, {el,[{alt,'FromDate', {'#PCDATA',char}, [],1,1,true,undefined}], 0,1,undefined,6}, {el,[{alt,'ToDate', {'#PCDATA',char}, [],1,1,true,undefined}], 0,1,undefined,7}, {el,[{alt,'Parameters', {'#PCDATA',char}, [],1,1,true,undefined}], 0,unbound,undefined,8}], [],undefined,undefined,8,1,1,undefined,undefined}], [{ns,"http://ws.jobdiva.com/BIData/","P0",unqualified}, {ns,"http://www.w3.org/2001/XMLSchema","xsd",qualified}], "http://ws.jobdiva.com/BIData/",[],false,skip}

exit with error with message: Struct doesn't match model: recordtype not expected: P0:getBIData

That provides to error on soap client and finally to error in my app. Problem

It's look like erlsom_write:findAlternative/3 try to find parent elemnt of root element in XML.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/willemdj/erlsom/issues/72?email_source=notifications&email_token=AABGHRYW77ZLI62AAXX55M3P2D2BJA5CNFSM4HXI5QV2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4GZB7B6A, or mute the thread https://github.com/notifications/unsubscribe-auth/AABGHR7LOK3T2HV4HWYIHO3P2D2BJANCNFSM4HXI5QVQ .

Koziolek commented 5 years ago

@willemdj WSDL https://ws.jobdiva.com/axis2/services/BIData?wsdl

I generated client following this post https://medium.com/@larryweya/soap-in-elixir-with-soap-64400c585f69

Code that call API. You could use any data in record because if you put something wrong then response is nice error message (I tested that via SoapUI).


defmodule JobDiva.ApiClient do
  @moduledoc false

  require Record
  import Record, only: [defrecord: 3, extract: 2]

 defrecord :bi_data_rec,
            "P0:getBIData",
            extract(:"P0:getBIData", from: "include/BIData.hrl")

  def get_bi_data() do
    bi_data_rec(
      MetricName: "*****",
      ClientID: "***",
      Username: "***",
      Password: "****",
      FromDate: "****",
      ToDate: "****"
    )
    |>:"BIData?wsdl_client".getBIData([],[])
    |> extract_results
  end

  defp extract_results({:error, inf, msg}), do: {:error, [inf, msg]}
  defp extract_results({:ok, _code, _headers, _, queryResponse, _, _body}) do
    {:ok, []}
  end
end
willemdj commented 5 years ago

I think it may be a problem related to the way SOAP is used from elixir. I can't help you with that.

I tested this using Erlang and as far as I can see it works fine:

1> soap:wsdl2erlang("BIData.wsdl", [{service,"BIData"},{port,"BIDataSOAP"},{generate,client},{namespaces,[{"http://ws.jobdiva.com/BIData/",undefined}]},{generate_tests,none},{http_client,soap_client_inets},{client_name,"BIData_client"},{strict,true}]).
==> Generated file BIData_client.erl
==> Generated file BIData.hrl
ok
2> c("BIData_client.erl").
{ok,'BIData_client'}
3> rr("BIData.hrl").
['ColumnNames','DataSet','Pair','RowData',faultcode,
 faultdetail,faultreason,getBIData,getBIDataResponse,qname,
 soap_fault_1_1,soap_fault_1_2]
4> 'BIData_client':getBIData(#getBIData{'MetricName' = "**", 'ClientID' = "**", 'Username' = "**", 'Password' = "**"}, [], []).
{ok,200,
    [{"date","Fri, 21 Jun 2019 07:14:58 GMT"},
     {"vary","Accept-Encoding"},
     {"content-length","327"},
     {"content-type","text/xml;charset=utf-8"}],
    [],
    #getBIDataResponse{'Message' = "Error: Invalid Request Parameter (For input string: \"**\")",
                       'Data' = undefined},
    [],
    <<"<?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xm"...>>}