dapper91 / pydantic-xml

python xml for humans
https://pydantic-xml.readthedocs.io
The Unlicense
141 stars 14 forks source link

Why isn't this working? #185

Closed conradogarciaberrotaran closed 2 months ago

conradogarciaberrotaran commented 2 months ago

Hi, thanks for the awesome tool!

I'm having problems with this XML:

VALID_XML_STR = """
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing">
   <env:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Body-FvRvW0qxCvSOh5eKkoz1dw22">
      <Response_DataList xmlns="http://xmlns.oracle.com/response_data">
         <receipt_id>2d333732363034313130363032313133</receipt_id>
         <total_records>2</total_records>
         <total_accepted>0</total_accepted>
         <total_rejected>2</total_rejected>
         <response_record>
            <batch_id>21</batch_id>
            <record_id>23343</record_id>
            <error_desc>Service date/time cannot exceed one year from date of service</error_desc>
         </response_record>
         <response_record>
            <batch_id>21</batch_id>
            <record_id>23352</record_id>
            <error_desc>Service date/time cannot exceed one year from date of service</error_desc>
         </response_record>
      </Response_DataList>
   </env:Body>
</env:Envelope>
"""

I have this models:

class ResponseRecord(BaseXmlModel, tag="response_record"):
    batch_id: str | None
    record_id: str | None
    error_desc: str | None

class ResponseDataList(BaseXmlModel, nsmap={"": "http://xmlns.oracle.com/response_data"}, tag="Response_DataList"):
    receipt_id: str = element(tag="receipt_id")
    total_records: NonNegativeInt = element(tag="total_records")
    total_accepted: NonNegativeInt = element(tag="total_accepted")
    total_rejected: NonNegativeInt = element(tag="total_rejected")
    records: List[ResponseRecord] = element(tag="response_record")

class Body(BaseXmlModel, tag="Body"):
    response_data_list: ResponseDataList = element(tag="Response_DataList")

class Envelope(
    BaseXmlModel,
    nsmap={"env": "http://schemas.xmlsoap.org/soap/envelope/"},
    tag="Envelope",
    ns="env",
):
    body: Body = element(tag="Body")

But i'm getting this errors:

E           pydantic_core._pydantic_core.ValidationError: 4 validation errors for Envelope
E           body.response_data_list.records.0.record_id
E             [line -1]: Field required [type=missing, input_value={'batch_id': '\n            '}, input_type=dict]
E           body.response_data_list.records.0.error_desc
E             [line -1]: Field required [type=missing, input_value={'batch_id': '\n            '}, input_type=dict]
E           body.response_data_list.records.1.record_id
E             [line -1]: Field required [type=missing, input_value={'batch_id': '\n            '}, input_type=dict]
E           body.response_data_list.records.1.error_desc
E             [line -1]: Field required [type=missing, input_value={'batch_id': '\n            '}, input_type=dict]

Not sure what's going on. I've tried many things. Any suggestions?

Thanks!

dapper91 commented 2 months ago

@conradogarciaberrotaran Hi,

Thanks for your feedback.

The ResponseRecord model is incorrect:

  1. batch_id, record_id, error_desc are sub-elements of the response_record, so that fields must be defined as elements:
class ResponseRecord(BaseXmlModel, tag="response_record"):
    batch_id: str | None = element()
    record_id: str | None = element()
    error_desc: str | None = element()
  1. Namespaces are not inherited by nested models so they must be defined explicitly:
class ResponseRecord(BaseXmlModel, tag="response_record", nsmap={"": "http://xmlns.oracle.com/response_data"}):
    batch_id: str | None = element()
    record_id: str | None = element()
    error_desc: str | None = element()

This code should work:

class ResponseRecord(BaseXmlModel, tag="response_record", nsmap={"": "http://xmlns.oracle.com/response_data"}):
    batch_id: str | None = element()
    record_id: str | None = element()
    error_desc: str | None = element()

class ResponseDataList(BaseXmlModel, nsmap={"": "http://xmlns.oracle.com/response_data"}, tag="Response_DataList"):
    receipt_id: str = element(tag="receipt_id")
    total_records: NonNegativeInt = element(tag="total_records")
    total_accepted: NonNegativeInt = element(tag="total_accepted")
    total_rejected: NonNegativeInt = element(tag="total_rejected")
    records: List[ResponseRecord] = element(tag="response_record")

class Body(BaseXmlModel, tag="Body"):
    response_data_list: ResponseDataList = element(tag="Response_DataList")

class Envelope(
    BaseXmlModel,
    nsmap={"env": "http://schemas.xmlsoap.org/soap/envelope/"},
    tag="Envelope",
    ns="env",
):
    body: Body = element(tag="Body")
conradogarciaberrotaran commented 2 months ago

You're correct. it worked.

Thank so much for the quick reply!