amzn / selling-partner-api-models

This repository contains OpenAPI models for developers to use when developing software to call Selling Partner APIs.
Apache License 2.0
570 stars 730 forks source link

[MISC] Feeds: Generate classes using JAXB-XJC for amzn-envelope.xsd #2558

Closed minifreak closed 9 months ago

minifreak commented 1 year ago

This is basically a guide about how to map amzn-envelope.xsd and all its included XSDs into java classes using the JAXB-XJC tool. I will describe as much as possible the whole process, the issues I've been encountering and current caveats. Bare in mind that the purpose of this post is to improve the metodology, fix issues and obviously help others whom may been stuck with this rather complex task. Worth to mention that there might be better ways to achieve this, or handle it differently (no JAXB for instance) so, it would be great to share it here in order to have alternatives. That said, I am aware that mapping the whole schema might not be necessary in most cases. In my scenario it is, as I am supposed to deliver a generic entreprise integration (to be used for different types of businesses with different needs). Anyhow, I will work on a way to use the schemas based on the Feed Types required, trying to avoid more changes on them while keeping consistency, ofc. However, since all of the schemas included in amzn-envelope.xsd are in the same namespace (empty ns, btw), I haven't figured yet how to achieve that.

Requirements

Command line

/<path-to-jaxb>/xjc.sh -d ../src -p com.example.feeds.pojo ./xsd_4_1/amzn-envelope.xsd -catalog catalog.xml -b bindings.xml -extension -nv

Ant

ant crashes when I use xjc ant task, likely because the schema is huge so it requires forking. Hence the direct java call which allows that precisely.

    <path id="local.xjc.class.path">
        <fileset dir="<lib_path>" includes="jaxb-core-4.0.1.jar"/>
        <fileset dir="<lib_path>" includes="jaxb-impl-4.0.1.jar"/>
        <fileset dir="<lib_path>" includes="jaxb-xjc-4.0.1.jar"/>
        <fileset dir="<lib_path>" includes="jakarta.xml.bind-api-4.0.0.jar"/>
        <fileset dir="<lib_path>" includes="jakarta.activation-api-2.1.0.jar"/>
    </path>

<target name="jaxb-xjc-pojo-generator">       
<!--        <xjc destdir="src" removeOldOutput="yes"-->
<!--             package="com.example.feeds.pojo"-->
<!--             catalog="config/catalog.xml"-->
<!--             binding="config/bindings.xml"-->
<!--             schema="config/xsd_4_1/amzn-envelope.xsd">-->
<!--            <schema dir="config/xsd_4_1/" includes="amzn-envelope.xsd" />-->
<!--            <depends dir="schema" includes="*.xsd"/>-->
<!--            <produces dir="src/com/example/feeds/pojo"-->
<!--                      includes="**/*"/>-->
<!--            <arg value="-nv"/>-->
<!--            <arg value="-extension"/>-->
<!--            <arg value="-verbose"/>-->
<!--        </xjc>-->

        <java classname="com.sun.tools.xjc.XJCFacade" fork="true">
            <classpath>
                <path refid="local.xjc.class.path" />
            </classpath>
            <arg value="-d"/>
            <arg value="src"/>
            <arg value="-p"/>
            <arg value="com.example.feeds.pojo"/>
            <arg value="-catalog"/>
            <arg value="config/catalog.xml"/>
            <arg value="config/xsd_4_1/amzn-envelope.xsd"/>
            <arg value="-b"/>
            <arg value="./config/bindings.xml"/>
            <arg value="-nv" />
            <arg value="-extension" />
        </java>
    </target>

Maven

TBD

Bindings

<jxb:bindings
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jxb="https://jakarta.ee/xml/ns/jaxb"
        xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
        jxb:extensionBindingPrefixes="xjc"
        xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb
            http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
        version="3.0">

    <jxb:globalBindings typesafeEnumMaxMembers="2100" enableJavaNamingConventions="false" underscoreBinding="asCharInWord">
        <xjc:superClass name="com.example.feeds.misc.AmazonSPFeedsPojo"/>
    </jxb:globalBindings>

    <jxb:bindings schemaLocation="./xsd_4_1/amzn-base.xsd">->
        <jxb:bindings node="//xsd:complexType[@name='Customer']">
            <jxb:class name="CustomerBase"/>
        </jxb:bindings>
    </jxb:bindings>

    <jxb:bindings schemaLocation="./xsd_4_1/Video.xsd">
        <jxb:bindings node="//xsd:element[@name='VideoDVD']/xsd:complexType/xsd:sequence/xsd:element[@name='BBFCRating']">
            <jxb:property name="bbfcRatingTypeDVD"/>
        </jxb:bindings>
        <jxb:bindings node="//xsd:element[@name='VideoVHS']/xsd:complexType/xsd:sequence/xsd:element[@name='BBFCRating']">
            <jxb:property name="bbfcRatingTypeVHS"/>
        </jxb:bindings>
        <jxb:bindings node="//xsd:simpleType[@name='BBFC_Rating_Type']">
            <jxb:typesafeEnumClass name="BBFCRatingTypeVideo"/>
        </jxb:bindings>
    </jxb:bindings>

    <jxb:bindings schemaLocation="./xsd_4_1/SWVG.xsd">
        <jxb:bindings node="//xsd:element[@name='Software']/xsd:complexType/xsd:sequence/xsd:element[@name='BbfcRating']">
            <jxb:property name="bbfcRatingSoftware"/>
        </jxb:bindings>
        <jxb:bindings node="//xsd:element[@name='SoftwareGames']/xsd:complexType/xsd:sequence/xsd:element[@name='BbfcRating']">
            <jxb:property name="bbfcRatingSoftwareGames"/>
        </jxb:bindings>
        <jxb:bindings node="//xsd:element[@name='VideoGames']/xsd:complexType/xsd:sequence/xsd:element[@name='BbfcRating']">
            <jxb:property name="bbfcRatingVideoGames"/>
        </jxb:bindings>

        <jxb:bindings node="//xsd:element[@name='Software']/xsd:complexType/xsd:sequence/xsd:element[@name='ESRBDescriptors']">
            <jxb:property name="esrbDescriptorsStr"/>
        </jxb:bindings>
        <jxb:bindings node="//xsd:element[@name='VideoGames']/xsd:complexType/xsd:sequence/xsd:element[@name='ESRBDescriptors']">
            <jxb:property name="esrbDescriptorsStr"/>
        </jxb:bindings>
    </jxb:bindings>

    <jxb:bindings schemaLocation="./xsd_4_1/CameraPhoto.xsd">
        <jxb:bindings node="//xsd:element[@type='MusicDVDRegionType']" multiple="true">
            <jxb:class name="MusicDVDRegionType" implClass="com.example.feeds.pojo.Music#MusicDVDRegionType"/>
            <jxb:property name="musicDVDRegionType"/>
        </jxb:bindings>
    </jxb:bindings>
    <jxb:bindings schemaLocation="./xsd_4_1/CE.xsd">
        <jxb:bindings node="//xsd:element[@name='DVDRegion']">
            <jxb:property name="dvdRegionCE"/>
        </jxb:bindings>
    </jxb:bindings>

    <jxb:bindings schemaLocation="./xsd_4_1/ProductAttributes.xsd">
        <jxb:bindings node="//xsd:element[@name='voltage']">
            <jxb:property name="VoltageProductAttribute"/>
            <jxb:class name="VoltageProductAttribute"/>
        </jxb:bindings>
        <jxb:bindings node="//xsd:element[@name='capacitance']">
            <jxb:property name="CapacitanceProductAttribute"/>
            <jxb:class name="CapacitanceProductAttribute"/>
        </jxb:bindings>
        <jxb:bindings node="//xsd:element[@name='inductance']">
            <jxb:property name="InductanceProductAttribute"/>
            <jxb:class name="InductanceProductAttribute"/>
        </jxb:bindings>
        <jxb:bindings node="//xsd:element[@name='resistance']">
            <jxb:property name="ResistanceProductAttribute"/>
            <jxb:class name="ResistanceProductAttribute"/>
        </jxb:bindings>
        <jxb:bindings node="//xsd:element[@name='frequency']">
            <jxb:property name="FrequencyProductAttribute"/>
            <jxb:class name="FrequencyProductAttribute"/>
        </jxb:bindings>
        <jxb:bindings node="//xsd:element[@name='denomination']">
            <jxb:factoryMethod name="DenominationProductAttribute"/>
        </jxb:bindings>
    </jxb:bindings>

    <!-- FIXME: I don't like this, would be better to use jxb:class. NOTE: BillingDataType must be created manually, just an empty class 
 for now -->
    <jxb:bindings schemaLocation="./xsd_4_1/PurchaseConfirmation.xsd" mapSimpleTypeDef="true">
        <jxb:bindings node=".//xsd:element[@type='BillingDataType']" multiple="true">
            <jxb:javaType name="com.example.feeds.misc.BillingDataType" hasNsContext="false"/>
        </jxb:bindings>
    </jxb:bindings>

    <jxb:bindings schemaLocation="./xsd_4_1/FoodServiceAndJanSan.xsd">
        <jxb:bindings node="//xsd:element[@name='FoodServiceAndJanSan']/xsd:complexType/xsd:sequence/xsd:element[@name='ProductType']/xsd:complexType/xsd:choice/xsd:element[@name='FoodServiceAndJanSan']/xsd:complexType">
            <jxb:class name="FoodServiceAndJanSanPT"/>
        </jxb:bindings>
    </jxb:bindings>
    <jxb:bindings schemaLocation="./xsd_4_1/GiftCards.xsd">
        <jxb:bindings node="//xsd:element[@name='GiftCard']/xsd:complexType/xsd:sequence/xsd:element[@name='ProductType']/xsd:complexType/xsd:choice/xsd:element[@name='GiftCard']/xsd:complexType">
            <jxb:class name="GiftCardPT"/>
        </jxb:bindings>
    </jxb:bindings>
    <jxb:bindings schemaLocation="./xsd_4_1/Home.xsd">
        <jxb:bindings node="//xsd:element[@name='Home']/xsd:complexType/xsd:sequence/xsd:element[@name='ProductType']/xsd:complexType/xsd:choice/xsd:element[@name='Home']/xsd:complexType">
            <jxb:class name="HomePT"/>
        </jxb:bindings>
    </jxb:bindings>
    <jxb:bindings schemaLocation="./xsd_4_1/MechanicalFasteners.xsd">
        <jxb:bindings node="//xsd:element[@name='MechanicalFasteners']/xsd:complexType/xsd:sequence/xsd:element[@name='ProductType']/xsd:complexType/xsd:choice/xsd:element[@name='MechanicalFasteners']/xsd:complexType">
            <jxb:class name="MechanicalFastenersPT"/>
        </jxb:bindings>
    </jxb:bindings>
</jxb:bindings>

XSD changes

In order to map properly all the schemas, I had to make several changes in some XSDs:

<xsd:include schemaLocation="ProductImage.xsd"/><!-- IMPORTANT NOTE: Only available in 1.9 version -->
<xsd:include schemaLocation="Relationship.xsd"/><!-- IMPORTANT NOTE: Only available in 1.9 version -->
<xsd:include schemaLocation="SettlementReport.xsd"/><!-- IMPORTANT NOTE: Only available in 1.9 version -->
<xsd:include schemaLocation="TestOrderRequest.xsd"/><!-- IMPORTANT NOTE: Only available in 1.9 version -->
<xsd:include schemaLocation="WebstoreItem.xsd"/><!-- IMPORTANT NOTE: Only available in 1.9 version -->
<xsd:element name="AmazonEnvelope">
    <xsd:complexType>
        <xsd:sequence>
            <xsd:element name="ExternalDocumentID" type="IDNumber" minOccurs="0"/>
            <xsd:element ref="Header"/>
            <xsd:element name="MessageType">
                <xsd:simpleType>
                    <xsd:restriction base="xsd:string">
                        <xsd:enumeration value="CatPIL"/>
                        <xsd:enumeration value="CharacterData"/>
                        <xsd:enumeration value="Customer"/>
                        <xsd:enumeration value="CustomerReport"/>
                        <xsd:enumeration value="EnhancedContent"/>
                        <xsd:enumeration value="ExternalCustomer"/>
                        <xsd:enumeration value="ExternalOrder"/>
                        <xsd:enumeration value="FulfillmentCenter"/>
                        <xsd:enumeration value="FulfillmentOrderRequest"/>
                        <xsd:enumeration value="FulfillmentOrderCancellationRequest"/>
                        <xsd:enumeration value="Image"/>
                        <xsd:enumeration value="Inventory"/>
                        <xsd:enumeration value="InvoiceConfirmation"/>
                        <xsd:enumeration value="Item"/>
                        <xsd:enumeration value="MSVat"/>
                        <xsd:enumeration value="Local"/>
                        <xsd:enumeration value="Loyalty"/>
                        <xsd:enumeration value="MultiChannelOrderReport"/>
                        <xsd:enumeration value="NavigationReport"/>
                        <xsd:enumeration value="Offer"/>
                        <xsd:enumeration value="OrderAcknowledgement"/>
                        <xsd:enumeration value="OrderAdjustment"/>
                        <xsd:enumeration value="OrderFulfillment"/>
                        <xsd:enumeration value="OrderSourcingOnDemand"/>
                        <xsd:enumeration value="OrderNotificationReport"/>
                        <xsd:enumeration value="OrderReport"/>
                        <xsd:enumeration value="Override"/>
                        <xsd:enumeration value="PendingOrderReport"/>
                        <xsd:enumeration value="PointOfSale"/>
                        <xsd:enumeration value="Price"/>
                        <xsd:enumeration value="TradeInPrice"/>
                        <xsd:enumeration value="ProcessingReport"/>
                        <xsd:enumeration value="Product"/>
                        <xsd:enumeration value="ProductImage"/>
                        <xsd:enumeration value="Promotion"/>
                        <xsd:enumeration value="PurchaseConfirmation"/>
                        <xsd:enumeration value="ACES"/>
                        <xsd:enumeration value="PIES"/>
                        <xsd:enumeration value="Relationship"/>
                        <xsd:enumeration value="ReverseItem"/>
                        <xsd:enumeration value="RichContent"/>
                        <xsd:enumeration value="SalesHistory"/>
                        <xsd:enumeration value="SalesAdjustment"/>
                        <xsd:enumeration value="SettlementReport"/>
                        <xsd:enumeration value="StandardProduct"/>
                        <xsd:enumeration value="TestOrderRequest"/>
                        <xsd:enumeration value="Store"/>
                        <xsd:enumeration value="StoreStockMovement"/>
                        <xsd:enumeration value="WebstoreItem"/>
                    </xsd:restriction>
                </xsd:simpleType>
            </xsd:element>
            <xsd:element ref="MarketplaceName" minOccurs="0">
                <xsd:annotation>
                    <xsd:documentation> The MarketplaceName is only supported for Override
                        feeds. If included here, the MarketplaceName will apply to all messages
                        in the feed. </xsd:documentation>
                </xsd:annotation>
            </xsd:element>
            <xsd:element name="MarketplaceID" type="xsd:positiveInteger" minOccurs="0">
                <xsd:annotation>
                    <xsd:documentation>             
                        The MarketplaceID is only supported for Price feeds.
                        If included here, the MarketplaceName will apply to
                        all messages in the feed.       
                    </xsd:documentation>
                </xsd:annotation>
            </xsd:element>
            <xsd:element name="PurgeAndReplace" type="xsd:boolean" minOccurs="0"/>
            <xsd:element name="EffectiveDate" type="xsd:dateTime" minOccurs="0"/>
            <xsd:element name="Message" maxOccurs="unbounded">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="MessageID" type="IDNumber"/>
                        <xsd:element name="OperationType" minOccurs="0">
                            <xsd:simpleType>
                                <xsd:restriction base="xsd:string">
                                    <xsd:enumeration value="Update"/>
                                    <xsd:enumeration value="Delete"/>
                                    <xsd:enumeration value="PartialUpdate"/>
                                </xsd:restriction>
                            </xsd:simpleType>
                        </xsd:element>
                        <xsd:choice>
                            <xsd:element name="AutoPartsItem" type="AutoPartsItem"/>
                            <xsd:element ref="CatPIL"/>
                            <xsd:element ref="CharacterData"/>
                            <xsd:element ref="Customer"/>
                            <xsd:element ref="FulfillmentCenter"/>
                            <xsd:element ref="FulfillmentOrderRequest"/>
                            <xsd:element ref="FulfillmentOrderCancellationRequest"/>
                            <xsd:element ref="Image"/>
                            <xsd:element ref="Inventory"/>
                            <xsd:element ref="Item"/>
                            <xsd:element ref="Loyalty"/>
                            <xsd:element ref="MultiChannelOrderReport"/>
                            <xsd:element ref="NavigationReport"/>
                            <xsd:element ref="Offer"/>
                            <xsd:element ref="OrderAcknowledgement"/>
                            <xsd:element ref="OrderAdjustment"/>
                            <xsd:element ref="OrderFulfillment"/>
                            <xsd:element ref="OrderSourcingOnDemand"/>
                            <xsd:element ref="OrderNotificationReport"/>
                            <xsd:element ref="OrderReport"/>
                            <xsd:element ref="Override"/>
                            <xsd:element ref="Price"/>
                            <xsd:element ref="ProcessingReport"/>
                            <xsd:element ref="Product"/>
                            <xsd:element ref="ProductImage"/>
                            <xsd:element ref="Relationship"/>
                            <xsd:element ref="SettlementReport"/>
                            <xsd:element ref="TestOrderRequest"/>
                            <xsd:element ref="WebstoreItem"/>
                            <xsd:element ref="PurchaseConfirmation"/>
                        </xsd:choice>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
        </xsd:sequence>
    </xsd:complexType>
</xsd:element>

</xsd:schema>

- PurchaseConfirmation.xsd

<?xml version="1.0"?>

... ``` - OrderReport.xsd ``` ... Based on http://www.w3.org/TR/xmlenc-core ... ``` ### Caveats - I haven't attempted to perform this in a more granular way, leaving out schemas that might not be necessary. However, I am not sure what the consequences might be. I guess it's a matter of trial and error. Having the schemas separated in different namespaces in the XSDs could be helpful imo. - Coudn't make `xjc ... -catalog ` to rewrite `xsd_1_9/` path to `xsd_4_1/` for filesystem paths. Without that working properly, it's not possible to use relative paths under `amzn-envelope.xsd` so there's no other way than copy them over from 1_9 to 4_1. It might be possible to do it the other way around, but haven't tried that (4_1 -> 1_9) Here's an example of a catalog.xml I've been playing with: ``` ``` Again, ideally having different namespaces would be really helpful for this I believe. That doesn't change the fact that all the schemas must exists for any given version. The ones that no longer exists, mustn't be included at all. In this way one could just map the classes directly from https://images-na.ssl-images-amazon.com/images/G/01/rainier/help/xsd/release_4_1/amzn-envelope.xsd or work with local copies without requiring all this mambo-jambo. - The commented stuff under `OrderReport.xsd` that affects `xenc-schema.xsd` may result in errors later on while sending the report through the AmazonSP API. - Generated classes sum up 3600+. Some of them, like **ObjectFactory** is 70k long. Dunno, seems too much and barely manageable. May also have an impact performance wise. - Ant xjc task crashes for me, so I've been forced to use java ant task instead and fork the process. However, I'd like to find a solution to make use of `removeOldOutput`, for instance. ### Disclaimer It's possible I missed some stuff or something may be inaccurate. Therefore, I will fill missing parts and keep updating this post as I get feedback or find issues.
AhmedX6 commented 1 year ago

Hello,

Is it possible for you to give me a sample of AmazonEnvelope to upload feeds and get my products online ready to be sell? EAN/ASIN - Quantity - Price - Condition .. ?

Thanks for your help

minifreak commented 1 year ago

Hi, I can, bare in mind though that I still can't upload the document because I got stuck with a Forbidden error every time I try. Basically, I can go through the 1st and 2nd steps described in the official guide but not the 3rd. Note that I'm using production endpoints here 'cause the static sandbox just sucks big time. The example I can give you belongs to POST_PRODUCT_DATA feed type, but I'm in a position to generate all feed types quite easily (due to JAXB code generation I described in the post) although not sure they will functional as AmazonSP Feed API, as well as the others, behave like black boxes in lots of cases and documentation is far from being great.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AmazonEnvelope>
    <Header>
        <DocumentVersion>1.01</DocumentVersion>
        <MerchantIdentifier>XXXXXXXXXXXX</MerchantIdentifier>
    </Header>
    <MessageType>Product</MessageType>
    <PurgeAndReplace>false</PurgeAndReplace>
    <Message>
        <MessageID>1</MessageID>
        <OperationType>Update</OperationType>
        <Product>
            <SKU>PRODUCT_A</SKU>
            <DescriptionData>
                <Title>Product A Title</Title>
            </DescriptionData>
            <ProductData/>
        </Product>
    </Message>
    <Message>
        <MessageID>2</MessageID>
        <OperationType>Update</OperationType>
        <Product>
            <SKU>PRODUCT_B</SKU>
            <DescriptionData>
                <Title>Product B Title</Title>
            </DescriptionData>
            <ProductData/>
        </Product>
    </Message>
....
</AmazonEnvelope>

As you can see, it is very similar to what you can find in the official guide. However, I'm aware that stuff like ASIN is missing, that's why I'm currently working on integrating Catalog Items API in order to sync stuff and get things like ASINs and whatnot. Also, I assumed that you can send multiple Messages within an AmazonEnvelope but that's just an assumption based on how the xsd is defined, couldn't find anywhere in the doc about that matter.

AhmedX6 commented 1 year ago

For me the documentation is not quite clear,

I tried with an amz envelop like following :

<?xml version="1.0" encoding="utf-8"?>
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
    <Header>
        <DocumentVersion>1.01</DocumentVersion>
        <MerchantIdentifier>MERCHANTID</MerchantIdentifier>
    </Header>
    <MessageType>Product</MessageType>
    <PurgeAndReplace>true</PurgeAndReplace>
    <Message>
        <MessageID>1</MessageID>
        <OperationType>Update</OperationType>
        <Product>
            <SKU>sku</SKU>
            <Condition>
                <ConditionType>New</ConditionType>
            </Condition>
            <StandardProductID>
                <Type>ASIN</Type>
                <Value>B082PQXYLM</Value>
            </StandardProductID>
            <Price>
                <StandardPrice currency="EUR">81.07</StandardPrice>
            </Price>
            <Quantity>1</Quantity>
        </Product>
    </Message>
</AmazonEnvelope>

Here is the processing report summary :

0 90012 Fatal The required sku field is missing from the file. To correct this error, download the template again and use the new copy, or insert the required field into your existing file.

You was not able to upload to seller central ?

I'm uploading like that: image

minifreak commented 1 year ago

So, you can actually upload the document, meaning you are good for the 1-3 steps but fails in the 4th step, correct? If so, you are ahead of me, I still can't upload the document. I'm trying with a different feed type though (POST_PRODUCT_DATA). That probably makes a difference

AhmedX6 commented 1 year ago

@minifreak do you have any update ?

minifreak commented 1 year ago

@minifreak do you have any update ?

Yes, sorta. I can upload the document now, it was a silly mistake with the content type, which is clearly stated in the documentation. However, since I'm using production endpoints, I don't want to create a feed with my entire inventory, hence I'm currently trimming it down just to one dummy product (message). Once I get it, I will try all feed product types and see how it goes. Will get back to you later. today with the results.

AhmedX6 commented 1 year ago

Hello @minifreak is it possible to contact me on my email ? contact@a2bsoft.net I would appreciate you share me how did you proceed to do all that.

Thanks a lot

github-actions[bot] commented 10 months ago

This is a very old issue that is probably not getting as much attention as it deserves. We encourage you to check if this is still an issue after the latest release and if you find that this is still a problem, please feel free to open a new issue and make a reference to this one.

github-actions[bot] commented 9 months ago

closed for inactivity