pythonxmlelementtree

XML Placement Problem with xml.etree.ElementTree


I'm using Python's xml.etree.ElementTree to generate XML, and I’m encountering an issue where an XML subelement appears under a different tag than intended.

My function looks like this:

def generate_product (parentElement, products: dict):
    for product_id, product_detail in products.items():
        product = ET.SubElement(parentElement, "PRODUCT")

        supplier_pid = ET.SubElement(product, "SUPPLIER_PID")
        supplier_pid.text = product_detail["supplier_pid"]

        product_details = ET.SubElement(product, "PRODUCT_DETAILS")

        description_short = ET.SubElement(product_details, "DESCRIPTION_SHORT")
        description_short.text = product_detail["description_short"]
        ean = ET.SubElement(product_details, "EAN")
        ean.text = product_detail["ean"]
        manufacturer_pid = ET.SubElement(product_details, "MANUFACTURER_PID")
        manufacturer_pid.text = product_detail["manufacturer_pid"]
        manufacturer_name = ET.SubElement(product_details, "MANUFACTURER_NAME")
        manufacturer_name.text = product_detail["manufacturer_name"]
        delivery_time = ET.SubElement(product_details, "DELIVERY_TIME")
        delivery_time.text = product_detail["delivery_time"]

        product_features = ET.SubElement(product, "PRODUCT_FEATURES")

        reference_feature_system_name = ET.SubElement(product_features, "REFERENCE_FEATURE_SYSTEM_NAME")
        reference_feature_system_name.text = product_detail["reference_feature_system_name"]
        reference_feature_group_id = ET.SubElement(product_features, "REFERENCE_FEATURE_GROUP_ID")
        reference_feature_group_id.text = product_detail["reference_feature_group_id"]

        product_order_details = ET.SubElement(product, "PRODUCT_ORDER_DETAILS")

        order_unit = ET.SubElement(product_order_details, "ORDER_UNIT")
        order_unit.text = product_detail["order_unit"]
        price_quantity = ET.SubElement(product_order_details, "PRICE_QUANTITY")
        price_quantity.text = product_detail["price_quantity"]
        quantity_min = ET.SubElement(product_order_details, "QUANTITY_MIN")
        quantity_min.text = product_detail["quantity_min"]
        quantity_interval = ET.SubElement(product_order_details, "QUANTITY_INTERVAL")
        quantity_interval.text = product_detail["quantity_interval"]

        product_price_details = ET.SubElement(product, "PRODUCT_PRICE_DETAILS")

        product_price_details_start_datetime = ET.SubElement(product_price_details, "DATETIME", attrib={"type":"valid_start_date"})

        price_start_date = ET.SubElement(product_price_details_start_datetime, "DATE")
        price_start_date.text = product_detail["valid_start_date"]

        product_price_details_end_datetime = ET.SubElement(product_price_details, "DATETIME", attrib={"type":"valid_end_date"})

        price_end_date = ET.SubElement(product_price_details_end_datetime, "DATE")
        price_end_date.text = product_detail["valid_end_date"]

        if product_detail["daily_price"] == "TRUE":
            daily_price = ET.SubElement(product_price_details, "DAILY_PRICE")
            daily_price.text = "true"

        product_price = ET.SubElement(product_price_details, "PRODUCT_PRICE", attrib={"price_type":f"{product_detail['price_type']}"})

        price_amount = ET.SubElement(product_price, "PRICE_AMOUNT")
        price_amount.text = product_detail["price_amount"]
        price_currency = ET.SubElement(product_price, "PRICE_CURRENCY")
        price_currency.text = product_detail["price_currency"]
        tax = ET.SubElement(product_price, "TAX")
        tax.text = product_detail["tax"]

        mime_info = ET.SubElement(product, "MIME_INFO")

        mime = ET.SubElement(mime_info, "MIME")

        mime_type = ET.SubElement(mime, "MIME_TYPE")
        mime_type.text = product_detail["mime_type"]
        mime_source = ET.SubElement(mime, "MIME_SOURCE")
        mime_source.text = product_detail["mime_source"]
        mime_descr = ET.SubElement(mime, "MIME_DESCR")
        mime_descr.text = product_detail["mime_descr"]
        mime_purpose = ET.SubElement(mime, "MIME_PURPOSE")
        mime_purpose.text = product_detail["mime_purpose"]
        mime_order = ET.SubElement(mime, "MIME_ORDER")
        mime_order.text = product_detail["mime_order"]

And the placement problem happens at this place in the code:

        product_price_details = ET.SubElement(product, "PRODUCT_PRICE_DETAILS")

        product_price_details_start_datetime = ET.SubElement(product_price_details, "DATETIME", attrib={"type":"valid_start_date"})

        price_start_date = ET.SubElement(product_price_details_start_datetime, "DATE")
        price_start_date.text = product_detail["valid_start_date"]

        product_price_details_end_datetime = ET.SubElement(product_price_details, "DATETIME", attrib={"type":"valid_end_date"})

        price_end_date = ET.SubElement(product_price_details_end_datetime, "DATE")
        price_end_date.text = product_detail["valid_end_date"]

        if product_detail["daily_price"] == "TRUE":
            daily_price = ET.SubElement(product_price_details, "DAILY_PRICE")
            daily_price.text = "true"

When I run this code, the DAILY_PRICE element ends up inside the DATETIME element with type valid_end_date, instead of being placed directly under PRODUCT_PRICE_DETAILS.

      <PRODUCT_PRICE_DETAILS>
        <DATETIME type="valid_start_date">
          <DATE>2024-08-14</DATE>
        </DATETIME>
        <DATETIME type="valid_end_date">
          <DATE>2024-09-30</DATE>
          <DAILY_PRICE>TRUE</DAILY_PRICE>
        </DATETIME>
     </PRODUCT_PRICE_DETAILS>

How can I ensure that DAILY_PRICE is correctly placed directly under PRODUCT_PRICE_DETAILS and not inside DATETIME?


Solution

  • Change the order, like:

    product_price_details = ET.SubElement(product, "PRODUCT_PRICE_DETAILS")
    
    if product_detail["daily_price"] == "TRUE":
        daily_price = ET.SubElement(product_price_details, "DAILY_PRICE")
        daily_price.text = "true"
    
    product_price_details_start_datetime = ET.SubElement(product_price_details, "DATETIME", attrib={"type":"valid_start_date"})
    
    price_start_date = ET.SubElement(product_price_details_start_datetime, "DATE")
    price_start_date.text = product_detail["valid_start_date"]
    
    product_price_details_end_datetime = ET.SubElement(product_price_details, "DATETIME", attrib={"type":"valid_end_date"})
    
    price_end_date = ET.SubElement(product_price_details_end_datetime, "DATE")
    price_end_date.text = product_detail["valid_end_date"]
    
    product_price = ET.SubElement(product_price_details, "PRODUCT_PRICE", attrib={"price_type":f"{product_detail['price_type']}"})
    

    Output:

    <PRODUCT_PRICE_DETAILS>
        <DAILY_PRICE>true</DAILY_PRICE>
        <DATETIME type="valid_start_date">
          <DATE>2024</DATE>
        </DATETIME>
        <DATETIME type="valid_end_date">
          <DATE>2025</DATE>
        </DATETIME>
        <PRODUCT_PRICE price_type="EURO" />
    </PRODUCT_PRICE_DETAILS>