How do I create a product with additional attributes in Magento via Soap/Java

I actually had to patch app/code/core/Mage/Catalog/Model/Product/Api/V2.php to get this call to work in version 1.6.2.0.

The existing code checks for either a "single_data" or "multi_data" attribute under the 'additional_attributes' property and tries to iterate over the those as an associative array:

if (property_exists($productData, 'additional_attributes')) {
        if (property_exists($productData->additional_attributes, 'single_data')) {
            foreach ($productData->additional_attributes->single_data as $_attribute) {
                $_attrCode = $_attribute->key;
                $productData->$_attrCode = $_attribute->value;
            }
        }
        if (property_exists($productData->additional_attributes, 'multi_data')) {
            foreach ($productData->additional_attributes->multi_data as $_attribute) {
                $_attrCode = $_attribute->key;
                $productData->$_attrCode = $_attribute->value;
            }
        }
}

I think that the issue is that since we're passing the associative array structure:

<item>
  <key>...</key>
  <value>...</value>
</item>
<item>
  <key>...</key>
  <value>...</value>
</item>

directly under the additional_attribtutes block then it's really the additional_attributes property that must be iterated over to pull the attribute key/values, so I added third if block:

 if (gettype($productData->additional_attributes) == 'array') {
            foreach ($productData->additional_attributes as $k => $v) {
                    $_attrCode = $k;
                    $productData->$_attrCode = $v;
            }
  }

With that code in place my custom attributes are being added/updated.

Here's a sample request:

 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"   xmlns:urn="urn:Magento">
 <soapenv:Header/>
 <soapenv:Body>
   <urn:catalogProductUpdateRequestParam>
     <sessionId>100fe1f9d0518b0fd0ed49cc460c1fa6</sessionId>
     <productId>1</productId>
     <productData>
        <name>product 3</name>
        <!--  the custom attributes to updated.... 
        -->
        <additional_attributes>
           <complexObjectArray>
              <key>feed_id</key>
              <value>56920</value>
           </complexObjectArray>
           <complexObjectArray>
              <key>feed_active</key>
              <value>1</value>
           </complexObjectArray>
        </additional_attributes>
      </productData>
    </urn:catalogProductUpdateRequestParam>
  </soapenv:Body>
</soapenv:Envelope>

For the V2 SOAP API it appears that we need to nest the additional_attributes in a multi_data or single_data layer?

Looking at app/code/core/Mage/Catalog/Model/Product/Api/V2.php #256 I think we need to use

$manufacturer = new stdClass();
$manufacturer->key = "manufacturer";
$manufacturer->value = "20";
$additionalAttrs['single_data'][] = $manufacturer;

or

$manufacturer = new stdClass();
$manufacturer->key = "manufacturer";
$manufacturer->value = "20";
$additionalAttrs['multi_data'][] = $manufacturer;

to be used like:

    $productData = new stdClass();
    $additionalAttrs = array();

            // manufacturer from one of the two above ^

    $productData->name                   = $data['name']; 
    $productData->description            = $data['description'];
    $productData->short_description      = $data['short_description'];
    $productData->weight                 = 0;
    $productData->status                 = 2; // 1 = active
    $productData->visibility             = 4; //visible in search/catalog
    $productData->category_ids           = $data['categories']; 
    $productData->price                  = $data['price'];
    $productData->tax_class_id           = 2; // 2=standard
    $productData->additional_attributes  = $additionalAttrs;

    // Create new product
    try {
        $proxy->catalogProductCreate($sessionId, 'virtual', 9, $sku, $productData); // 9 is courses
    } catch (SoapFault $e) {
        print $e->getMessage();  //Internal Error. Please see log for details.
        exit();
    }