You are viewing a plain text version of this content. The canonical link for it is here.
Posted to c-commits@axis.apache.org by ro...@apache.org on 2026/01/13 22:20:48 UTC

(axis-axis2-c-core) 02/06: AXIS2C-1224: Add schema complexType parsing with empty sequence support

This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-c-core.git

commit b714b79f622f962622c2b1ff28c2dbf042fb6795
Author: Robert Lazarski <ro...@gmail.com>
AuthorDate: Tue Jan 13 10:21:22 2026 -1000

    AXIS2C-1224: Add schema complexType parsing with empty sequence support
    
    The native WSDL2C generator now parses all complexTypes from the WSDL
    schema and generates proper ADB classes for each, including types with
    empty sequences (common for exception types like AccessDeniedException).
    
    Changes:
    - Add wsdl2c_complex_type_t structure to hold parsed complexType info
    - Add parse_wsdl_complex_types() to parse all named complexTypes
    - Parse sequence elements within complexTypes with name, type, minOccurs,
      maxOccurs, and nillable attributes
    - Detect empty sequences (<sequence/> or <sequence></sequence>)
    - Add generate_adb_complex_types() to generate ADB classes from schema
    - Map XSD types to C types (string, int, boolean, double, dateTime, etc.)
    - Empty sequences generate structs with _unused placeholder field
    - Generate proper getters/setters for types with properties
    
    This fixes the issue where WSDL2C failed on WSDLs containing complexTypes
    with empty sequences, which are commonly used for exception types.
    
    Co-Authored-By: Claude Opus 4.5 <no...@anthropic.com>
---
 .../wsdl/AXIS2C-1224-EmptySequenceTest.wsdl        | 104 ++++++
 tools/codegen/native/include/wsdl2c_native.h       |  16 +
 tools/codegen/native/src/stub_generator.c          | 365 +++++++++++++++++++++
 tools/codegen/native/src/wsdl_parser.c             | 197 +++++++++++
 4 files changed, 682 insertions(+)

diff --git a/test/resources/wsdl/AXIS2C-1224-EmptySequenceTest.wsdl b/test/resources/wsdl/AXIS2C-1224-EmptySequenceTest.wsdl
new file mode 100644
index 000000000..7f5e68d6e
--- /dev/null
+++ b/test/resources/wsdl/AXIS2C-1224-EmptySequenceTest.wsdl
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  AXIS2C-1224: Test WSDL for empty sequence handling.
+
+  The issue is that WSDL2C fails when encountering empty <sequence/> elements
+  in complexType definitions. This is common for exception types and marker
+  types that have no properties.
+-->
+<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                  xmlns:tns="http://www.example.org/EmptySequenceTest/"
+                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                  name="EmptySequenceTest"
+                  targetNamespace="http://www.example.org/EmptySequenceTest/">
+  <wsdl:types>
+    <xsd:schema targetNamespace="http://www.example.org/EmptySequenceTest/">
+      <!-- Request type with normal sequence -->
+      <xsd:element name="TestOperation" type="tns:TestOperationType"/>
+      <xsd:element name="TestOperationResponse" type="tns:TestOperationResponseType"/>
+
+      <!-- Normal type with elements -->
+      <xsd:complexType name="TestOperationType">
+        <xsd:sequence>
+          <xsd:element name="input" type="xsd:string"/>
+        </xsd:sequence>
+      </xsd:complexType>
+
+      <!-- Normal response type -->
+      <xsd:complexType name="TestOperationResponseType">
+        <xsd:sequence>
+          <xsd:element name="result" type="xsd:string"/>
+        </xsd:sequence>
+      </xsd:complexType>
+
+      <!-- AXIS2C-1224: Empty sequence - self-closing -->
+      <xsd:complexType name="AccessDeniedException">
+        <xsd:sequence/>
+      </xsd:complexType>
+
+      <!-- AXIS2C-1224: Empty sequence - open/close tags -->
+      <xsd:complexType name="OperationFailedException">
+        <xsd:sequence>
+        </xsd:sequence>
+      </xsd:complexType>
+
+      <!-- AXIS2C-1224: Another empty sequence exception -->
+      <xsd:complexType name="InvalidParametersException">
+        <xsd:sequence/>
+      </xsd:complexType>
+
+      <!-- AXIS2C-1224: Marker type with empty sequence -->
+      <xsd:complexType name="EmptyMarkerType">
+        <xsd:sequence/>
+      </xsd:complexType>
+    </xsd:schema>
+  </wsdl:types>
+
+  <wsdl:message name="TestOperationRequest">
+    <wsdl:part element="tns:TestOperation" name="parameters"/>
+  </wsdl:message>
+  <wsdl:message name="TestOperationResponse">
+    <wsdl:part element="tns:TestOperationResponse" name="parameters"/>
+  </wsdl:message>
+  <wsdl:message name="AccessDeniedFault">
+    <wsdl:part name="fault" type="tns:AccessDeniedException"/>
+  </wsdl:message>
+  <wsdl:message name="OperationFailedFault">
+    <wsdl:part name="fault" type="tns:OperationFailedException"/>
+  </wsdl:message>
+
+  <wsdl:portType name="EmptySequenceTest">
+    <wsdl:operation name="TestOperation">
+      <wsdl:input message="tns:TestOperationRequest"/>
+      <wsdl:output message="tns:TestOperationResponse"/>
+      <wsdl:fault name="AccessDenied" message="tns:AccessDeniedFault"/>
+      <wsdl:fault name="OperationFailed" message="tns:OperationFailedFault"/>
+    </wsdl:operation>
+  </wsdl:portType>
+
+  <wsdl:binding name="EmptySequenceTestSOAP" type="tns:EmptySequenceTest">
+    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+    <wsdl:operation name="TestOperation">
+      <soap:operation soapAction="http://www.example.org/EmptySequenceTest/TestOperation"/>
+      <wsdl:input>
+        <soap:body use="literal"/>
+      </wsdl:input>
+      <wsdl:output>
+        <soap:body use="literal"/>
+      </wsdl:output>
+      <wsdl:fault name="AccessDenied">
+        <soap:fault name="AccessDenied" use="literal"/>
+      </wsdl:fault>
+      <wsdl:fault name="OperationFailed">
+        <soap:fault name="OperationFailed" use="literal"/>
+      </wsdl:fault>
+    </wsdl:operation>
+  </wsdl:binding>
+
+  <wsdl:service name="EmptySequenceTest">
+    <wsdl:port binding="tns:EmptySequenceTestSOAP" name="EmptySequenceTestSOAP">
+      <soap:address location="http://localhost:8080/axis2/services/EmptySequenceTest"/>
+    </wsdl:port>
+  </wsdl:service>
+</wsdl:definitions>
diff --git a/tools/codegen/native/include/wsdl2c_native.h b/tools/codegen/native/include/wsdl2c_native.h
index d490d24d4..8002ca18a 100644
--- a/tools/codegen/native/include/wsdl2c_native.h
+++ b/tools/codegen/native/include/wsdl2c_native.h
@@ -198,6 +198,20 @@ typedef struct wsdl2c_schema_element {
     int max_occurs;                 /**< maxOccurs value (-1 = unbounded) */
 } wsdl2c_schema_element_t;
 
+/**
+ * @brief Complex type structure (parsed from XSD schema)
+ * AXIS2C-1224: Supports complexTypes including those with empty sequences.
+ * An empty sequence (no elements) generates a struct with no properties.
+ */
+typedef struct wsdl2c_complex_type {
+    axis2_char_t *name;             /**< Type name (original XML name) */
+    axis2_char_t *c_name;           /**< Sanitized C identifier */
+    axutil_array_list_t *elements;  /**< Child elements (wsdl2c_schema_element_t*) */
+    axis2_bool_t is_empty_sequence; /**< True if complexType has empty <sequence/> */
+    axis2_bool_t has_sequence;      /**< True if complexType contains a sequence */
+    axis2_char_t *base_type;        /**< Base type for extension/restriction */
+} wsdl2c_complex_type_t;
+
 /**
  * @brief Operation structure (parsed from WSDL)
  * Contains operation name and soapAction extracted from binding.
@@ -221,7 +235,9 @@ typedef struct wsdl2c_wsdl {
     axutil_array_list_t *operations;    /**< Operations array (wsdl2c_operation_t*) */
     axutil_array_list_t *messages;      /**< Messages array */
     axutil_array_list_t *schema_elements; /**< Schema elements (wsdl2c_schema_element_t*) */
+    axutil_array_list_t *complex_types; /**< Complex types (wsdl2c_complex_type_t*) - AXIS2C-1224 */
     axis2_bool_t has_any_type;          /**< True if any xsd:any elements found (AXIS2C-1580) */
+    axis2_bool_t has_empty_sequences;   /**< True if any empty sequences found (AXIS2C-1224) */
     void *schema_node;                   /**< Schema information */
 } wsdl2c_wsdl_t;
 
diff --git a/tools/codegen/native/src/stub_generator.c b/tools/codegen/native/src/stub_generator.c
index 3adfed596..66ac3fdd8 100644
--- a/tools/codegen/native/src/stub_generator.c
+++ b/tools/codegen/native/src/stub_generator.c
@@ -763,6 +763,364 @@ generate_stub_source(wsdl2c_context_t *context, const axutil_env_t *env)
     return AXIS2_SUCCESS;
 }
 
+/* AXIS2C-1224: Generate ADB classes for parsed schema complexTypes
+ * This handles all complexTypes including those with empty sequences.
+ * Empty sequences generate structs with no properties but valid create/free/serialize functions.
+ */
+static axis2_status_t
+generate_adb_complex_types(wsdl2c_context_t *context, const axutil_env_t *env)
+{
+    int type_count, i;
+
+    if (!context->wsdl || !context->wsdl->complex_types) {
+        return AXIS2_SUCCESS; /* No complex types to generate */
+    }
+
+    type_count = axutil_array_list_size(context->wsdl->complex_types, env);
+    if (type_count == 0) {
+        return AXIS2_SUCCESS;
+    }
+
+    printf("AXIS2C-1224: Generating ADB classes for %d schema complexTypes\n", type_count);
+
+    for (i = 0; i < type_count; i++) {
+        wsdl2c_complex_type_t *complex_type = axutil_array_list_get(
+            context->wsdl->complex_types, env, i);
+        if (!complex_type || !complex_type->name) continue;
+
+        const char *type_name = complex_type->c_name ? complex_type->c_name : complex_type->name;
+        FILE *header_file = NULL, *source_file = NULL;
+        axis2_char_t *header_path = NULL, *source_path = NULL;
+        int elem_count = 0;
+
+        if (complex_type->elements) {
+            elem_count = axutil_array_list_size(complex_type->elements, env);
+        }
+
+        /* Create ADB header file */
+        header_path = AXIS2_MALLOC(env->allocator,
+            strlen(context->options->output_dir) + strlen(type_name) + 50);
+        sprintf(header_path, "%s/src/adb_%s.h", context->options->output_dir, type_name);
+
+        header_file = fopen(header_path, "w");
+        if (!header_file) {
+            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
+                "Failed to create ADB header: %s", header_path);
+            AXIS2_FREE(env->allocator, header_path);
+            continue;
+        }
+
+        /* Write header file */
+        fprintf(header_file, "/**\n");
+        fprintf(header_file, " * adb_%s.h\n", type_name);
+        fprintf(header_file, " *\n");
+        fprintf(header_file, " * This file was auto-generated from WSDL schema\n");
+        fprintf(header_file, " * by the Apache Axis2/C Native version: 1.0.0\n");
+        if (complex_type->is_empty_sequence) {
+            fprintf(header_file, " * AXIS2C-1224: ComplexType with empty sequence\n");
+        }
+        fprintf(header_file, " */\n\n");
+
+        fprintf(header_file, "#ifndef ADB_%s_H\n", axutil_string_toupper(type_name, env));
+        fprintf(header_file, "#define ADB_%s_H\n\n", axutil_string_toupper(type_name, env));
+        fprintf(header_file, "#include <stdio.h>\n");
+        fprintf(header_file, "#include <axiom.h>\n");
+        fprintf(header_file, "#include <axutil_utils.h>\n");
+        fprintf(header_file, "#include <axiom_soap.h>\n");
+        fprintf(header_file, "#include <axis2_client.h>\n\n");
+        fprintf(header_file, "#ifdef __cplusplus\n");
+        fprintf(header_file, "extern \"C\" {\n");
+        fprintf(header_file, "#endif\n\n");
+
+        /* Structure forward declaration */
+        fprintf(header_file, "typedef struct adb_%s adb_%s_t;\n\n", type_name, type_name);
+
+        /* Constructor */
+        fprintf(header_file, "/**\n");
+        fprintf(header_file, " * Constructor for %s\n", type_name);
+        fprintf(header_file, " */\n");
+        fprintf(header_file, "adb_%s_t* AXIS2_CALL\n", type_name);
+        fprintf(header_file, "adb_%s_create(const axutil_env_t *env);\n\n", type_name);
+
+        /* Destructor */
+        fprintf(header_file, "/**\n");
+        fprintf(header_file, " * Free %s\n", type_name);
+        fprintf(header_file, " */\n");
+        fprintf(header_file, "axis2_status_t AXIS2_CALL\n");
+        fprintf(header_file, "adb_%s_free(adb_%s_t* _this, const axutil_env_t *env);\n\n",
+                type_name, type_name);
+
+        /* Generate property accessors for elements (if any) */
+        if (elem_count > 0) {
+            int j;
+            for (j = 0; j < elem_count; j++) {
+                wsdl2c_schema_element_t *elem = axutil_array_list_get(
+                    complex_type->elements, env, j);
+                if (!elem || !elem->name) continue;
+
+                const char *elem_name = elem->c_name ? elem->c_name : elem->name;
+                const char *c_type = "axis2_char_t*"; /* Default to string */
+
+                /* Map XSD types to C types */
+                if (elem->type) {
+                    if (strstr(elem->type, "int") || strstr(elem->type, "Integer")) {
+                        c_type = "int";
+                    } else if (strstr(elem->type, "boolean") || strstr(elem->type, "bool")) {
+                        c_type = "axis2_bool_t";
+                    } else if (strstr(elem->type, "double") || strstr(elem->type, "float")) {
+                        c_type = "double";
+                    } else if (strstr(elem->type, "dateTime")) {
+                        c_type = "axutil_date_time_t*";
+                    } else if (strstr(elem->type, "base64Binary")) {
+                        c_type = "axiom_node_t*";
+                    }
+                }
+
+                /* Getter */
+                fprintf(header_file, "/**\n");
+                fprintf(header_file, " * Getter for %s\n", elem_name);
+                fprintf(header_file, " */\n");
+                fprintf(header_file, "%s AXIS2_CALL\n", c_type);
+                fprintf(header_file, "adb_%s_get_%s(adb_%s_t* _this, const axutil_env_t *env);\n\n",
+                        type_name, elem_name, type_name);
+
+                /* Setter */
+                fprintf(header_file, "/**\n");
+                fprintf(header_file, " * Setter for %s\n", elem_name);
+                fprintf(header_file, " */\n");
+                fprintf(header_file, "axis2_status_t AXIS2_CALL\n");
+                fprintf(header_file, "adb_%s_set_%s(adb_%s_t* _this, const axutil_env_t *env, %s value);\n\n",
+                        type_name, elem_name, type_name, c_type);
+            }
+        }
+
+        /* Serialize function */
+        fprintf(header_file, "/**\n");
+        fprintf(header_file, " * Serialize to axiom_node\n");
+        fprintf(header_file, " */\n");
+        fprintf(header_file, "axiom_node_t* AXIS2_CALL\n");
+        fprintf(header_file, "adb_%s_serialize(adb_%s_t* _this, const axutil_env_t *env,\n",
+                type_name, type_name);
+        fprintf(header_file, "                axiom_node_t* parent, axiom_element_t *parent_element,\n");
+        fprintf(header_file, "                int parent_tag_closed, axutil_hash_t* namespaces,\n");
+        fprintf(header_file, "                int *next_ns_index);\n\n");
+
+        /* Deserialize function */
+        fprintf(header_file, "/**\n");
+        fprintf(header_file, " * Deserialize from axiom_node\n");
+        fprintf(header_file, " */\n");
+        fprintf(header_file, "adb_%s_t* AXIS2_CALL\n", type_name);
+        fprintf(header_file, "adb_%s_create_from_node(const axutil_env_t *env, axiom_node_t *node,\n",
+                type_name);
+        fprintf(header_file, "                       int dont_care_minoccurs);\n\n");
+
+        fprintf(header_file, "#ifdef __cplusplus\n");
+        fprintf(header_file, "}\n");
+        fprintf(header_file, "#endif\n\n");
+        fprintf(header_file, "#endif /* ADB_%s_H */\n", axutil_string_toupper(type_name, env));
+
+        fclose(header_file);
+
+        /* Create ADB source file */
+        source_path = AXIS2_MALLOC(env->allocator,
+            strlen(context->options->output_dir) + strlen(type_name) + 50);
+        sprintf(source_path, "%s/src/adb_%s.c", context->options->output_dir, type_name);
+
+        source_file = fopen(source_path, "w");
+        if (!source_file) {
+            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
+                "Failed to create ADB source: %s", source_path);
+            AXIS2_FREE(env->allocator, header_path);
+            AXIS2_FREE(env->allocator, source_path);
+            continue;
+        }
+
+        /* Write source file */
+        fprintf(source_file, "/**\n");
+        fprintf(source_file, " * adb_%s.c\n", type_name);
+        fprintf(source_file, " *\n");
+        fprintf(source_file, " * This file was auto-generated from WSDL schema\n");
+        fprintf(source_file, " * by the Apache Axis2/C Native version: 1.0.0\n");
+        if (complex_type->is_empty_sequence) {
+            fprintf(source_file, " * AXIS2C-1224: ComplexType with empty sequence\n");
+        }
+        fprintf(source_file, " */\n\n");
+        fprintf(source_file, "#include \"adb_%s.h\"\n\n", type_name);
+
+        /* Structure definition */
+        fprintf(source_file, "struct adb_%s {\n", type_name);
+        if (elem_count > 0) {
+            int j;
+            for (j = 0; j < elem_count; j++) {
+                wsdl2c_schema_element_t *elem = axutil_array_list_get(
+                    complex_type->elements, env, j);
+                if (!elem || !elem->name) continue;
+
+                const char *elem_name = elem->c_name ? elem->c_name : elem->name;
+                const char *c_type = "axis2_char_t*";
+
+                if (elem->type) {
+                    if (strstr(elem->type, "int") || strstr(elem->type, "Integer")) {
+                        c_type = "int";
+                    } else if (strstr(elem->type, "boolean") || strstr(elem->type, "bool")) {
+                        c_type = "axis2_bool_t";
+                    } else if (strstr(elem->type, "double") || strstr(elem->type, "float")) {
+                        c_type = "double";
+                    } else if (strstr(elem->type, "dateTime")) {
+                        c_type = "axutil_date_time_t*";
+                    } else if (strstr(elem->type, "base64Binary")) {
+                        c_type = "axiom_node_t*";
+                    }
+                }
+                fprintf(source_file, "    %s %s;\n", c_type, elem_name);
+            }
+        } else {
+            /* AXIS2C-1224: Empty sequence - add dummy field to avoid empty struct */
+            fprintf(source_file, "    /* AXIS2C-1224: Empty sequence - no properties */\n");
+            fprintf(source_file, "    int _unused; /* Placeholder to avoid empty struct */\n");
+        }
+        fprintf(source_file, "};\n\n");
+
+        /* Constructor implementation */
+        fprintf(source_file, "adb_%s_t* AXIS2_CALL\n", type_name);
+        fprintf(source_file, "adb_%s_create(const axutil_env_t *env)\n", type_name);
+        fprintf(source_file, "{\n");
+        fprintf(source_file, "    adb_%s_t *obj = NULL;\n", type_name);
+        fprintf(source_file, "    AXIS2_ENV_CHECK(env, NULL);\n");
+        fprintf(source_file, "    obj = AXIS2_MALLOC(env->allocator, sizeof(adb_%s_t));\n", type_name);
+        fprintf(source_file, "    if (!obj) {\n");
+        fprintf(source_file, "        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);\n");
+        fprintf(source_file, "        return NULL;\n");
+        fprintf(source_file, "    }\n");
+        fprintf(source_file, "    memset(obj, 0, sizeof(adb_%s_t));\n", type_name);
+        fprintf(source_file, "    return obj;\n");
+        fprintf(source_file, "}\n\n");
+
+        /* Destructor implementation */
+        fprintf(source_file, "axis2_status_t AXIS2_CALL\n");
+        fprintf(source_file, "adb_%s_free(adb_%s_t* _this, const axutil_env_t *env)\n",
+                type_name, type_name);
+        fprintf(source_file, "{\n");
+        fprintf(source_file, "    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);\n");
+        fprintf(source_file, "    if (!_this) {\n");
+        fprintf(source_file, "        return AXIS2_SUCCESS;\n");
+        fprintf(source_file, "    }\n");
+
+        /* Free string properties */
+        if (elem_count > 0) {
+            int j;
+            for (j = 0; j < elem_count; j++) {
+                wsdl2c_schema_element_t *elem = axutil_array_list_get(
+                    complex_type->elements, env, j);
+                if (!elem || !elem->name) continue;
+
+                const char *elem_name = elem->c_name ? elem->c_name : elem->name;
+                int is_string = 1;
+
+                if (elem->type) {
+                    if (strstr(elem->type, "int") || strstr(elem->type, "Integer") ||
+                        strstr(elem->type, "boolean") || strstr(elem->type, "bool") ||
+                        strstr(elem->type, "double") || strstr(elem->type, "float")) {
+                        is_string = 0;
+                    }
+                }
+
+                if (is_string) {
+                    fprintf(source_file, "    if (_this->%s) {\n", elem_name);
+                    fprintf(source_file, "        AXIS2_FREE(env->allocator, _this->%s);\n", elem_name);
+                    fprintf(source_file, "    }\n");
+                }
+            }
+        }
+
+        fprintf(source_file, "    AXIS2_FREE(env->allocator, _this);\n");
+        fprintf(source_file, "    return AXIS2_SUCCESS;\n");
+        fprintf(source_file, "}\n\n");
+
+        /* Property accessor implementations */
+        if (elem_count > 0) {
+            int j;
+            for (j = 0; j < elem_count; j++) {
+                wsdl2c_schema_element_t *elem = axutil_array_list_get(
+                    complex_type->elements, env, j);
+                if (!elem || !elem->name) continue;
+
+                const char *elem_name = elem->c_name ? elem->c_name : elem->name;
+                const char *c_type = "axis2_char_t*";
+                const char *default_val = "NULL";
+
+                if (elem->type) {
+                    if (strstr(elem->type, "int") || strstr(elem->type, "Integer")) {
+                        c_type = "int";
+                        default_val = "0";
+                    } else if (strstr(elem->type, "boolean") || strstr(elem->type, "bool")) {
+                        c_type = "axis2_bool_t";
+                        default_val = "AXIS2_FALSE";
+                    } else if (strstr(elem->type, "double") || strstr(elem->type, "float")) {
+                        c_type = "double";
+                        default_val = "0.0";
+                    }
+                }
+
+                /* Getter */
+                fprintf(source_file, "%s AXIS2_CALL\n", c_type);
+                fprintf(source_file, "adb_%s_get_%s(adb_%s_t* _this, const axutil_env_t *env)\n",
+                        type_name, elem_name, type_name);
+                fprintf(source_file, "{\n");
+                fprintf(source_file, "    AXIS2_ENV_CHECK(env, %s);\n", default_val);
+                fprintf(source_file, "    return _this ? _this->%s : %s;\n", elem_name, default_val);
+                fprintf(source_file, "}\n\n");
+
+                /* Setter */
+                fprintf(source_file, "axis2_status_t AXIS2_CALL\n");
+                fprintf(source_file, "adb_%s_set_%s(adb_%s_t* _this, const axutil_env_t *env, %s value)\n",
+                        type_name, elem_name, type_name, c_type);
+                fprintf(source_file, "{\n");
+                fprintf(source_file, "    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);\n");
+                fprintf(source_file, "    AXIS2_PARAM_CHECK(env->error, _this, AXIS2_FAILURE);\n");
+                fprintf(source_file, "    _this->%s = value;\n", elem_name);
+                fprintf(source_file, "    return AXIS2_SUCCESS;\n");
+                fprintf(source_file, "}\n\n");
+            }
+        }
+
+        /* Serialize implementation (placeholder) */
+        fprintf(source_file, "axiom_node_t* AXIS2_CALL\n");
+        fprintf(source_file, "adb_%s_serialize(adb_%s_t* _this, const axutil_env_t *env,\n",
+                type_name, type_name);
+        fprintf(source_file, "                axiom_node_t* parent, axiom_element_t *parent_element,\n");
+        fprintf(source_file, "                int parent_tag_closed, axutil_hash_t* namespaces,\n");
+        fprintf(source_file, "                int *next_ns_index)\n");
+        fprintf(source_file, "{\n");
+        fprintf(source_file, "    /* TODO: Implement full serialization */\n");
+        fprintf(source_file, "    return parent;\n");
+        fprintf(source_file, "}\n\n");
+
+        /* Deserialize implementation (placeholder) */
+        fprintf(source_file, "adb_%s_t* AXIS2_CALL\n", type_name);
+        fprintf(source_file, "adb_%s_create_from_node(const axutil_env_t *env, axiom_node_t *node,\n",
+                type_name);
+        fprintf(source_file, "                       int dont_care_minoccurs)\n");
+        fprintf(source_file, "{\n");
+        fprintf(source_file, "    /* TODO: Implement full deserialization */\n");
+        fprintf(source_file, "    return adb_%s_create(env);\n", type_name);
+        fprintf(source_file, "}\n");
+
+        fclose(source_file);
+
+        if (complex_type->is_empty_sequence) {
+            printf("  Generated ADB class for '%s' (empty sequence)\n", type_name);
+        } else {
+            printf("  Generated ADB class for '%s' (%d elements)\n", type_name, elem_count);
+        }
+
+        AXIS2_FREE(env->allocator, header_path);
+        AXIS2_FREE(env->allocator, source_path);
+    }
+
+    return AXIS2_SUCCESS;
+}
+
 /* Generate proper ADB class implementations - AXIS2C-1401: Use parsed operations */
 static axis2_status_t
 generate_adb_classes(wsdl2c_context_t *context, const axutil_env_t *env)
@@ -1367,6 +1725,13 @@ wsdl2c_generate_code(wsdl2c_context_t *context, const axutil_env_t *env)
         return AXIS2_FAILURE;
     }
 
+    /* AXIS2C-1224: Generate ADB classes for schema complexTypes (including empty sequences) */
+    status = generate_adb_complex_types(context, env);
+    if (status != AXIS2_SUCCESS) {
+        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Failed to generate ADB complex types");
+        return AXIS2_FAILURE;
+    }
+
     /* Generate stub source implementation */
     status = generate_stub_source(context, env);
     if (status != AXIS2_SUCCESS) {
diff --git a/tools/codegen/native/src/wsdl_parser.c b/tools/codegen/native/src/wsdl_parser.c
index a724b1089..b390f5fc2 100644
--- a/tools/codegen/native/src/wsdl_parser.c
+++ b/tools/codegen/native/src/wsdl_parser.c
@@ -277,6 +277,196 @@ parse_wsdl_bindings(wsdl2c_context_t *context, xmlXPathContextPtr xpath_ctx, con
     return AXIS2_SUCCESS;
 }
 
+/* AXIS2C-1224: Parse complexTypes from schema, including empty sequences */
+static axis2_status_t
+parse_wsdl_complex_types(wsdl2c_context_t *context, xmlXPathContextPtr xpath_ctx, const axutil_env_t *env)
+{
+    xmlXPathObjectPtr complex_types_result = NULL;
+    xmlNodeSetPtr complex_type_nodes = NULL;
+    int i;
+
+    AXIS2_PARAM_CHECK(env->error, context, AXIS2_FAILURE);
+    AXIS2_PARAM_CHECK(env->error, xpath_ctx, AXIS2_FAILURE);
+
+    /* Initialize complex types list */
+    context->wsdl->complex_types = axutil_array_list_create(env, 0);
+    context->wsdl->has_empty_sequences = AXIS2_FALSE;
+
+    /* Find all complexType elements in the schema */
+    complex_types_result = xmlXPathEvalExpression(
+        BAD_CAST "//xsd:complexType[@name] | //xs:complexType[@name]", xpath_ctx);
+
+    if (!complex_types_result || !complex_types_result->nodesetval ||
+        complex_types_result->nodesetval->nodeNr == 0) {
+        AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "No named complexTypes found in schema");
+        if (complex_types_result) {
+            xmlXPathFreeObject(complex_types_result);
+        }
+        return AXIS2_SUCCESS;
+    }
+
+    complex_type_nodes = complex_types_result->nodesetval;
+    AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI,
+        "AXIS2C-1224: Found %d complexTypes in schema", complex_type_nodes->nodeNr);
+
+    for (i = 0; i < complex_type_nodes->nodeNr; i++) {
+        xmlNodePtr complex_type_node = complex_type_nodes->nodeTab[i];
+        wsdl2c_complex_type_t *complex_type = NULL;
+        xmlChar *type_name = NULL;
+        xmlNodePtr child_node = NULL;
+        xmlNodePtr sequence_node = NULL;
+        int element_count = 0;
+
+        /* Get complexType name */
+        type_name = xmlGetProp(complex_type_node, BAD_CAST "name");
+        if (!type_name) {
+            continue;
+        }
+
+        /* Allocate and initialize complex type structure */
+        complex_type = AXIS2_MALLOC(env->allocator, sizeof(wsdl2c_complex_type_t));
+        memset(complex_type, 0, sizeof(wsdl2c_complex_type_t));
+
+        complex_type->name = axutil_strdup(env, (const axis2_char_t*)type_name);
+        complex_type->c_name = wsdl2c_sanitize_c_identifier(env, (const axis2_char_t*)type_name);
+        complex_type->elements = axutil_array_list_create(env, 0);
+        complex_type->is_empty_sequence = AXIS2_FALSE;
+        complex_type->has_sequence = AXIS2_FALSE;
+
+        /* Look for sequence element within the complexType */
+        for (child_node = complex_type_node->children; child_node; child_node = child_node->next) {
+            if (child_node->type != XML_ELEMENT_NODE) {
+                continue;
+            }
+
+            /* Check for direct sequence child */
+            if (xmlStrcmp(child_node->name, BAD_CAST "sequence") == 0) {
+                sequence_node = child_node;
+                complex_type->has_sequence = AXIS2_TRUE;
+                break;
+            }
+
+            /* Check for sequence inside complexContent/restriction or complexContent/extension */
+            if (xmlStrcmp(child_node->name, BAD_CAST "complexContent") == 0 ||
+                xmlStrcmp(child_node->name, BAD_CAST "simpleContent") == 0) {
+                xmlNodePtr content_child = NULL;
+                for (content_child = child_node->children; content_child; content_child = content_child->next) {
+                    if (content_child->type != XML_ELEMENT_NODE) {
+                        continue;
+                    }
+                    if (xmlStrcmp(content_child->name, BAD_CAST "restriction") == 0 ||
+                        xmlStrcmp(content_child->name, BAD_CAST "extension") == 0) {
+                        /* Get base type */
+                        xmlChar *base = xmlGetProp(content_child, BAD_CAST "base");
+                        if (base) {
+                            complex_type->base_type = axutil_strdup(env, (const axis2_char_t*)base);
+                            xmlFree(base);
+                        }
+                        /* Look for sequence inside */
+                        xmlNodePtr inner_child = NULL;
+                        for (inner_child = content_child->children; inner_child; inner_child = inner_child->next) {
+                            if (inner_child->type == XML_ELEMENT_NODE &&
+                                xmlStrcmp(inner_child->name, BAD_CAST "sequence") == 0) {
+                                sequence_node = inner_child;
+                                complex_type->has_sequence = AXIS2_TRUE;
+                                break;
+                            }
+                        }
+                    }
+                    if (sequence_node) break;
+                }
+            }
+            if (sequence_node) break;
+        }
+
+        /* Parse elements within the sequence */
+        if (sequence_node) {
+            xmlNodePtr elem_node = NULL;
+            for (elem_node = sequence_node->children; elem_node; elem_node = elem_node->next) {
+                if (elem_node->type == XML_ELEMENT_NODE &&
+                    xmlStrcmp(elem_node->name, BAD_CAST "element") == 0) {
+
+                    wsdl2c_schema_element_t *element = NULL;
+                    xmlChar *elem_name = NULL;
+                    xmlChar *elem_type = NULL;
+                    xmlChar *nillable = NULL;
+                    xmlChar *min_occurs = NULL;
+                    xmlChar *max_occurs = NULL;
+
+                    elem_name = xmlGetProp(elem_node, BAD_CAST "name");
+                    if (!elem_name) {
+                        continue;
+                    }
+
+                    element = AXIS2_MALLOC(env->allocator, sizeof(wsdl2c_schema_element_t));
+                    memset(element, 0, sizeof(wsdl2c_schema_element_t));
+
+                    element->name = axutil_strdup(env, (const axis2_char_t*)elem_name);
+                    element->c_name = wsdl2c_sanitize_c_identifier(env, (const axis2_char_t*)elem_name);
+                    element->is_any_type = AXIS2_FALSE;
+
+                    elem_type = xmlGetProp(elem_node, BAD_CAST "type");
+                    if (elem_type) {
+                        element->type = axutil_strdup(env, (const axis2_char_t*)elem_type);
+                        element->c_type = wsdl2c_sanitize_c_identifier(env, (const axis2_char_t*)elem_type);
+                        xmlFree(elem_type);
+                    }
+
+                    nillable = xmlGetProp(elem_node, BAD_CAST "nillable");
+                    element->is_nillable = (nillable && xmlStrcmp(nillable, BAD_CAST "true") == 0);
+                    if (nillable) xmlFree(nillable);
+
+                    min_occurs = xmlGetProp(elem_node, BAD_CAST "minOccurs");
+                    element->min_occurs = min_occurs ? atoi((const char*)min_occurs) : 1;
+                    if (min_occurs) xmlFree(min_occurs);
+
+                    max_occurs = xmlGetProp(elem_node, BAD_CAST "maxOccurs");
+                    if (max_occurs) {
+                        if (xmlStrcmp(max_occurs, BAD_CAST "unbounded") == 0) {
+                            element->max_occurs = -1;
+                        } else {
+                            element->max_occurs = atoi((const char*)max_occurs);
+                        }
+                        xmlFree(max_occurs);
+                    } else {
+                        element->max_occurs = 1;
+                    }
+
+                    axutil_array_list_add(complex_type->elements, env, element);
+                    element_count++;
+
+                    xmlFree(elem_name);
+                }
+            }
+
+            /* AXIS2C-1224: Check for empty sequence */
+            if (element_count == 0) {
+                complex_type->is_empty_sequence = AXIS2_TRUE;
+                context->wsdl->has_empty_sequences = AXIS2_TRUE;
+                AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI,
+                    "AXIS2C-1224: ComplexType '%s' has empty sequence", complex_type->name);
+            }
+        }
+
+        AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI,
+            "Parsed complexType '%s': %d elements, empty_sequence=%d, has_sequence=%d",
+            complex_type->name, element_count,
+            complex_type->is_empty_sequence, complex_type->has_sequence);
+
+        axutil_array_list_add(context->wsdl->complex_types, env, complex_type);
+        xmlFree(type_name);
+    }
+
+    xmlXPathFreeObject(complex_types_result);
+
+    AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI,
+        "AXIS2C-1224: Parsed %d complexTypes, %d have empty sequences",
+        axutil_array_list_size(context->wsdl->complex_types, env),
+        context->wsdl->has_empty_sequences ? 1 : 0);
+
+    return AXIS2_SUCCESS;
+}
+
 /* Parse schema elements to detect xsd:any types (AXIS2C-1580 fix) */
 static axis2_status_t
 parse_wsdl_schema(wsdl2c_context_t *context, xmlXPathContextPtr xpath_ctx, const axutil_env_t *env)
@@ -483,6 +673,13 @@ wsdl2c_parse_wsdl(wsdl2c_context_t *context, const axutil_env_t *env)
         goto cleanup;
     }
 
+    /* Parse complexTypes including empty sequences (AXIS2C-1224) */
+    status = parse_wsdl_complex_types(context, xpath_ctx, env);
+    if (status != AXIS2_SUCCESS) {
+        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Failed to parse WSDL complexTypes");
+        goto cleanup;
+    }
+
     AXIS2_LOG_INFO(env->log, AXIS2_LOG_SI, "Successfully parsed WSDL: %s",
                    context->options->wsdl_uri);