Conditional Assignment in XSD1.1 - xml-parsing

I have difficulty in writing a schema in XSD1.1. XML structure is already defined and used by many teams, so changing the xml is not an option to me.
May be I can't explain everything I tried here. XSD has evolved so much since start. I just hope some ppl can understand the problem clearly.. Would be great if someone can share their way of solving this problems..
How XML looks like (in simplified format)
<Node>
<Create Type="A">
<Attr Name="Type1" Val="123"/>
<Attr Name="Type2" Val="Water"/>
<Attr Name="Type3" Val="2019-12-01T08:00:00"/>
</Create>
<Create Type="B">
<Attr Name="TypeM" Val="OB123"/>
<Attr Name="Type2" Val="Fire"/>
<Attr Name="TypeK" Val="2019-12-01T08:00:00"/>
<Attr Name="TypeN" Val="11.567"/>
</Create>
</Node>
Problem:
Based on "Type" attribute in Element<Create> - Number of <Attr> Elements changes (There are 80 types). Each Type can allow specific set of child attr. In above e.g, when Type = A, it can have only 3 children and specifically Type1, Type2 and Type3.
Based on value of "Name" attribute in Element <Attr> , attribute "Val" can have range of Values. In Xml shown above, Type2 can have {Fire, Water}
Based on value of "Name" attribute in Element<Attr> , define the type of second attribute "Val" which can be (date,integer, string, float etc)
The XSD I am trying to write looks something like this (actual XSD and XML are huge)..
<xs:element name="Node" type="NodeType"/>
<xs:complexType name="NodeType">
<xs:sequence>
<xs:element name="Create">
<xs:alternative test="#Type = 'A'" type="AType"/>
<xs:alternative test="#Type = 'B'" type="BType"/>
<xs:alternative type="xs:error"/>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="AType">
<xs:sequence>
<xs:element name="Attr">
<xs:alternative test="#Name = 'Type1'" type="AttrType1"/>
<xs:alternative test="#Name = 'Type2'" type="AttrType2"/>
<xs:alternative test="#Name = 'Type3'" type="AttrType3"/>
<!--<xs:alternative type="xs:error"/> This line gives error while same line above passes validation-->
</xs:element>
</xs:sequence>
<xs:attribute name="Type" use="required" type="AllowedTypes"/> <!-- AllowedTypes is an enum of 80 values-->
<xs:assert test="(#Type = 'A')"/>
</xs:complexType>
<xs:complexType name="AttrType1">
<xs:attribute name="Name" type="xs:string"/>
<xs:attribute name="Val" type="xs:integer"/>
</xs:complexType>
<xs:complexType name="AttrType2">
<xs:attribute name="Name" type="xs:string"/>
<xs:attribute name="Val" type="xs:string"/>
<xs:assert test="(#Val = 'Fire' or #Val='Water')"/>
</xs:complexType>
<xs:complexType name="AttrType3">
<xs:attribute name="Name" type="xs:string"/>
<xs:attribute name="Val" type="xs:dateTime"/>
</xs:complexType>
<!-- Similarly written for BType -->
Thanks to this post - How to make type depend on attribute value using Conditional Type Assignment.
After spending days.. I am able to solve 3 problems (one at a time) stated above.. I am facing issue when I tried to club all 3 solutions in one nice compact xsd.
For Eg.. When I try to solve Problem 3, solution to problem 1 is messed up.
<Node>
<Create Type="A">
<Attr Name="Type1" Val="123"/>
<Attr Name="Type2" Val="Water"/>
<Attr Name="TypeZ" Val="2019-12-01T08:00:00"/>
</Create>
</Node>
<!-- I want XSD to throw error saying TypeZ is not allowed -->

I rectified xsd to solve all prob statements
<?xml version="1.0"?>
<xs:schema attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
xmlns:xerces="http://xerces.apache.org"
vc:minVersion="1.1">
<xs:element name="Node" type="NodeType"/>
<xs:complexType name="NodeType">
<xs:sequence>
<xs:element name="Create" maxOccurs="5">
<xs:alternative test="#Type = 'A'" type="AType"/>
<xs:alternative test="#Type = 'B'" type="BType"/>
<xs:alternative type="xs:error"/>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="AType">
<xs:sequence>
<xs:element name="Attr" maxOccurs="3">
<xs:alternative test="#Name = 'Type1'" type="AttrType1"/>
<xs:alternative test="#Name = 'Type2'" type="AttrType2"/>
<xs:alternative test="#Name = 'Type3'" type="AttrType3"/>
<xs:alternative type="xs:error"/>
</xs:element>
</xs:sequence>
<xs:attribute name="Type" use="required" type="xs:string"/>
<xs:assert test="(#Type = 'A')"/>
</xs:complexType>
<xs:complexType name="AttrType1">
<xs:attribute name="Name" type="xs:string"/>
<xs:attribute name="Val" type="xs:integer"/>
</xs:complexType>
<xs:complexType name="AttrType2">
<xs:attribute name="Name" type="xs:string"/>
<xs:attribute name="Val" type="xs:string"/>
<xs:assert test="(#Val = 'Fire' or #Val = 'Water')"/>
</xs:complexType>
<xs:complexType name="AttrType3">
<xs:attribute name="Name" type="xs:string"/>
<xs:attribute name="Val" type="xs:dateTime"/>
</xs:complexType>
<!-- Similarly written for BType -->
<xs:complexType name="BType">
<xs:sequence>
<xs:element name="Attr" maxOccurs="4">
<xs:alternative test="#Name = 'TypeM'" type="AttrTypeM"/>
<xs:alternative test="#Name = 'Type2'" type="AttrType2"/>
<xs:alternative test="#Name = 'TypeK'" type="AttrTypeK"/>
<xs:alternative test="#Name = 'TypeN'" type="AttrTypeN"/>
<xs:alternative type="xs:error"/>
</xs:element>
</xs:sequence>
<xs:attribute name="Type" use="required" type="xs:string"/>
<xs:assert test="(#Type = 'B')"/>
</xs:complexType>
<xs:complexType name="AttrTypeM">
<xs:attribute name="Name" type="xs:string"/>
<xs:attribute name="Val" type="xs:string"/>
</xs:complexType>
<xs:complexType name="AttrTypeK">
<xs:attribute name="Name" type="xs:string"/>
<xs:attribute name="Val" type="xs:dateTime"/>
</xs:complexType>
<xs:complexType name="AttrTypeN">
<xs:attribute name="Name" type="xs:string"/>
<xs:attribute name="Val" type="xs:float"/>
</xs:complexType>
</xs:schema>

Related

XSLT - How to tell if an element with specific attribute value is the last in the matching series

I have the following XSLT template
<xsl:template match="xsd:element[#name != '' and not(starts-with(#type, 'common:'))]">
<xsl:if test="position() != last()">
"<xsl:value-of select="#name"/>",
</xsl:if>
<xsl:if test="position() = last()">
"<xsl:value-of select="#name"/>"
</xsl:if>
</xsl:template>
that tries to match all elements with non empty name and it's type doesn't start with 'common:' then it will generate a comma separated list of these elements names.
so if applied to
<xsd:element name="One" type="String"/>
<xsd:element name="" type="String"/>
<xsd:OtherNode />
<xsd:element name="Two" type="common:Characters"/>
<xsd:element name="Three" type="Long"/>
<xsd:OtherNode />
it should generate
"One",
"Three"
notice that there is no comma after "Three"
but it sounds like there is something wrong with position() and last() as when printing its values it doesn't sounds to be correct and there is always a comma ','
"One",
"Three",
a complete sample of an input XML that will be processed by the XSLT is an XSD something like
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns:xsd="[h t t p] ://www.w3.org/2001/XMLSchema"
xmlns:common="[http]://abc.com/common/1.0">
<!-- definition of simple elements -->
<xsd:element name="orderperson" type="xsd:string"/>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="address" type="xsd:string"/>
<xsd:element name="city" type="xsd:string"/>
<xsd:element name="country" type="xsd:string"/>
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="note" type="xsd:string"/>
<xsd:element name="quantity" type="xsd:positiveInteger"/>
<xsd:element name="price" type="common:decimal"/>
<!-- definition of complex elements -->
<xsd:complexType name="shipto">
<xsd:sequence>
<xsd:element ref="name"/>
<xsd:element ref="address"/>
<xsd:element ref="city"/>
<xsd:element ref="country"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="item">
<xsd:sequence>
<xsd:element ref="title"/>
<xsd:element ref="note" minOccurs="0"/>
<xsd:element ref="quantity"/>
<xsd:element ref="price"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="shiporder">
<xsd:sequence>
<xsd:element ref="orderperson"/>
<xsd:element ref="shipto"/>
<xsd:element ref="item" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
and below is part of my XSLT after removing unrelated sections to keep it short
<xsl:stylesheet version="2.0"
xmlns:xsl="[http]://www.w3.org/1999/XSL/Transform"
xmlns:xsd="[http]://www.w3.org/2001/XMLSchema"
xmlns:xml="[http]://www.w3.org/XML/1998/namespace"
xmlns:common="[http]://abc.com/common/1.0">
<xsl:output method="text" media-type="text/xml" indent="yes" encoding="ISO-8859-1" />
<xsl:template match="/">
<xsl:call-template name="pre-properties"/>
<xsl:apply-templates/>
<xsl:call-template name="post-properties"/>
</xsl:template>
<xsl:template match="/xsd:schema/xsd:complexType/xsd:sequence/xsd:element"/>
<xsl:template name="pre-properties">
{
<xsl:template name="post-properties">
}
</xsl:template>
<xsl:template match="xsd:element[#name != '' and not(starts-with(#type, 'common:'))]">
<xsl:if test="position() != last()">
"<xsl:value-of select="#name"/>",
</xsl:if>
<xsl:if test="position() = last()">
"<xsl:value-of select="#name"/>"
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Move the check into a predicate e.g.
<xsl:template match="xsd:element[#name != '' and not(starts-with(#type, 'common:'))][position() != last()]">
"<xsl:value-of select="#name"/>",
</xsl:template>
<xsl:template match="xsd:element[#name != '' and not(starts-with(#type, 'common:'))][position() = last()]">
"<xsl:value-of select="#name"/>"
</xsl:template>
Or you might simply try
<xsl:value-of select="//xsd:element[#name != '' and not(starts-with(#type, 'common:'))]/concat('"', #name, '"')" separator=", "/>
where you want to output those values.

generate wsdl from legacy jax-ws client code

it´s possible to generate a WSDL using the JAX-WS client code only?
I have a legacy client code without wsdl inside, just remote wsdl uri, and I need to create mockservices for that client code and I need the WSDL to create this mocks.
Any idea?
For purpose of creating a mock/stub service provider, I believe it is better to download the original WSDL if you can. The risk with generating a WSDL from client generated code is the definite possibility the generated WSDL doesn't exactly match the original, which defeats the purpose of mocking or stubbing.
However, here were the steps I used to generate a WSDL from a generated JAX-WS client. I did have to hand-code an implementation class.
Original WSDL: http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL
Generate JAX-WS client:
wsimport -extension -keep http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL
This creates the sample JAX-WS client, which includes a generated service endpoint interface (SEI): com.cdyne.ws.weatherws.WeatherSoap
Oddly enough, we need an implementation class to run wsgen to generate a WSDL. I manually created an implementation class, declared it to implement the SEI, then copied all methods from the interface definition and gave each a return null; implementation.
package com.cdyne.ws.weatherws;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
#WebService(name = "WeatherSoap", targetNamespace = "http://ws.cdyne.com/WeatherWS/")
public class WeatherSoapStubImpl implements WeatherSoap {
/**
* Gets Information for each WeatherID
*
* #return
* returns com.cdyne.ws.weatherws.ArrayOfWeatherDescription
*/
#WebMethod(operationName = "GetWeatherInformation", action = "http://ws.cdyne.com/WeatherWS/GetWeatherInformation")
#WebResult(name = "GetWeatherInformationResult", targetNamespace = "http://ws.cdyne.com/WeatherWS/")
#RequestWrapper(localName = "GetWeatherInformation", targetNamespace = "http://ws.cdyne.com/WeatherWS/", className = "com.cdyne.ws.weatherws.GetWeatherInformation")
#ResponseWrapper(localName = "GetWeatherInformationResponse", targetNamespace = "http://ws.cdyne.com/WeatherWS/", className = "com.cdyne.ws.weatherws.GetWeatherInformationResponse")
public ArrayOfWeatherDescription getWeatherInformation() {
return null;
}
/**
* Allows you to get your City Forecast Over the Next 7 Days, which is updated hourly. U.S. Only
*
* #param zip
* #return
* returns com.cdyne.ws.weatherws.ForecastReturn
*/
#WebMethod(operationName = "GetCityForecastByZIP", action = "http://ws.cdyne.com/WeatherWS/GetCityForecastByZIP")
#WebResult(name = "GetCityForecastByZIPResult", targetNamespace = "http://ws.cdyne.com/WeatherWS/")
#RequestWrapper(localName = "GetCityForecastByZIP", targetNamespace = "http://ws.cdyne.com/WeatherWS/", className = "com.cdyne.ws.weatherws.GetCityForecastByZIP")
#ResponseWrapper(localName = "GetCityForecastByZIPResponse", targetNamespace = "http://ws.cdyne.com/WeatherWS/", className = "com.cdyne.ws.weatherws.GetCityForecastByZIPResponse")
public ForecastReturn getCityForecastByZIP(
#WebParam(name = "ZIP", targetNamespace = "http://ws.cdyne.com/WeatherWS/")
String zip) {
return null;
}
/**
* Allows you to get your City's Weather, which is updated hourly. U.S. Only
*
* #param zip
* #return
* returns com.cdyne.ws.weatherws.WeatherReturn
*/
#WebMethod(operationName = "GetCityWeatherByZIP", action = "http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP")
#WebResult(name = "GetCityWeatherByZIPResult", targetNamespace = "http://ws.cdyne.com/WeatherWS/")
#RequestWrapper(localName = "GetCityWeatherByZIP", targetNamespace = "http://ws.cdyne.com/WeatherWS/", className = "com.cdyne.ws.weatherws.GetCityWeatherByZIP")
#ResponseWrapper(localName = "GetCityWeatherByZIPResponse", targetNamespace = "http://ws.cdyne.com/WeatherWS/", className = "com.cdyne.ws.weatherws.GetCityWeatherByZIPResponse")
public WeatherReturn getCityWeatherByZIP(
#WebParam(name = "ZIP", targetNamespace = "http://ws.cdyne.com/WeatherWS/")
String zip) {
return null;
}
}
Compile the stub implementation class.
javac com/cdyne/ws/weatherws/WeatherSoapStubImpl.java
Generate the WSDL. This is where command line switches can be used to try to get as close as possible to the original.
wsgen -keep -cp . com.cdyne.ws.weatherws.WeatherSoapStubImpl -wsdl:Xsoap1.2 -extension -inlineSchemas
(creates WSDL in current dir):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Generated by JAX-WS RI (http://jax-ws.java.net). RI's version is JAX-WS RI 2.2.9-b130926.1035 svn-revision#8c29a9a53251ff741fca1664a8221dc876b2eac8. -->
<definitions targetNamespace="http://ws.cdyne.com/WeatherWS/" name="WeatherSoapStubImplService" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:tns="http://ws.cdyne.com/WeatherWS/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata">
<types>
<xs:schema elementFormDefault="qualified" version="1.0" targetNamespace="http://ws.cdyne.com/WeatherWS/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="GetCityForecastByZIP" type="tns:GetCityForecastByZIP"/>
<xs:element name="GetCityForecastByZIPResponse" type="tns:GetCityForecastByZIPResponse"/>
<xs:element name="GetCityWeatherByZIP" type="tns:GetCityWeatherByZIP"/>
<xs:element name="GetCityWeatherByZIPResponse" type="tns:GetCityWeatherByZIPResponse"/>
<xs:element name="GetWeatherInformation" type="tns:GetWeatherInformation"/>
<xs:element name="GetWeatherInformationResponse" type="tns:GetWeatherInformationResponse"/>
<xs:complexType name="GetWeatherInformation">
<xs:sequence/>
</xs:complexType>
<xs:complexType name="GetWeatherInformationResponse">
<xs:sequence>
<xs:element name="GetWeatherInformationResult" type="tns:ArrayOfWeatherDescription" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ArrayOfWeatherDescription">
<xs:sequence>
<xs:element name="WeatherDescription" type="tns:WeatherDescription" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="WeatherDescription">
<xs:sequence>
<xs:element name="WeatherID" type="xs:short"/>
<xs:element name="Description" type="xs:string" minOccurs="0"/>
<xs:element name="PictureURL" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GetCityWeatherByZIP">
<xs:sequence>
<xs:element name="ZIP" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GetCityWeatherByZIPResponse">
<xs:sequence>
<xs:element name="GetCityWeatherByZIPResult" type="tns:WeatherReturn" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="WeatherReturn">
<xs:sequence>
<xs:element name="Success" type="xs:boolean"/>
<xs:element name="ResponseText" type="xs:string" minOccurs="0"/>
<xs:element name="State" type="xs:string" minOccurs="0"/>
<xs:element name="City" type="xs:string" minOccurs="0"/>
<xs:element name="WeatherStationCity" type="xs:string" minOccurs="0"/>
<xs:element name="WeatherID" type="xs:short"/>
<xs:element name="Description" type="xs:string" minOccurs="0"/>
<xs:element name="Temperature" type="xs:string" minOccurs="0"/>
<xs:element name="RelativeHumidity" type="xs:string" minOccurs="0"/>
<xs:element name="Wind" type="xs:string" minOccurs="0"/>
<xs:element name="Pressure" type="xs:string" minOccurs="0"/>
<xs:element name="Visibility" type="xs:string" minOccurs="0"/>
<xs:element name="WindChill" type="xs:string" minOccurs="0"/>
<xs:element name="Remarks" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GetCityForecastByZIP">
<xs:sequence>
<xs:element name="ZIP" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="GetCityForecastByZIPResponse">
<xs:sequence>
<xs:element name="GetCityForecastByZIPResult" type="tns:ForecastReturn" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ForecastReturn">
<xs:sequence>
<xs:element name="Success" type="xs:boolean"/>
<xs:element name="ResponseText" type="xs:string" minOccurs="0"/>
<xs:element name="State" type="xs:string" minOccurs="0"/>
<xs:element name="City" type="xs:string" minOccurs="0"/>
<xs:element name="WeatherStationCity" type="xs:string" minOccurs="0"/>
<xs:element name="ForecastResult" type="tns:ArrayOfForecast" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ArrayOfForecast">
<xs:sequence>
<xs:element name="Forecast" type="tns:Forecast" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Forecast">
<xs:sequence>
<xs:element name="Date" type="xs:dateTime"/>
<xs:element name="WeatherID" type="xs:short"/>
<xs:element name="Desciption" type="xs:string" minOccurs="0"/>
<xs:element name="Temperatures" type="tns:temp"/>
<xs:element name="ProbabilityOfPrecipiation" type="tns:POP"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="temp">
<xs:sequence>
<xs:element name="MorningLow" type="xs:string" minOccurs="0"/>
<xs:element name="DaytimeHigh" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="POP">
<xs:sequence>
<xs:element name="Nighttime" type="xs:string" minOccurs="0"/>
<xs:element name="Daytime" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</types>
<message name="GetWeatherInformation">
<part name="parameters" element="tns:GetWeatherInformation"/>
</message>
<message name="GetWeatherInformationResponse">
<part name="parameters" element="tns:GetWeatherInformationResponse"/>
</message>
<message name="GetCityForecastByZIP">
<part name="parameters" element="tns:GetCityForecastByZIP"/>
</message>
<message name="GetCityForecastByZIPResponse">
<part name="parameters" element="tns:GetCityForecastByZIPResponse"/>
</message>
<message name="GetCityWeatherByZIP">
<part name="parameters" element="tns:GetCityWeatherByZIP"/>
</message>
<message name="GetCityWeatherByZIPResponse">
<part name="parameters" element="tns:GetCityWeatherByZIPResponse"/>
</message>
<portType name="WeatherSoap">
<operation name="GetWeatherInformation">
<input wsam:Action="http://ws.cdyne.com/WeatherWS/GetWeatherInformation" message="tns:GetWeatherInformation"/>
<output wsam:Action="http://ws.cdyne.com/WeatherWS/WeatherSoap/GetWeatherInformationResponse" message="tns:GetWeatherInformationResponse"/>
</operation>
<operation name="GetCityForecastByZIP">
<input wsam:Action="http://ws.cdyne.com/WeatherWS/GetCityForecastByZIP" message="tns:GetCityForecastByZIP"/>
<output wsam:Action="http://ws.cdyne.com/WeatherWS/WeatherSoap/GetCityForecastByZIPResponse" message="tns:GetCityForecastByZIPResponse"/>
</operation>
<operation name="GetCityWeatherByZIP">
<input wsam:Action="http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP" message="tns:GetCityWeatherByZIP"/>
<output wsam:Action="http://ws.cdyne.com/WeatherWS/WeatherSoap/GetCityWeatherByZIPResponse" message="tns:GetCityWeatherByZIPResponse"/>
</operation>
</portType>
<binding name="WeatherSoapPortBinding" type="tns:WeatherSoap">
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="GetWeatherInformation">
<soap12:operation soapAction="http://ws.cdyne.com/WeatherWS/GetWeatherInformation"/>
<input>
<soap12:body use="literal"/>
</input>
<output>
<soap12:body use="literal"/>
</output>
</operation>
<operation name="GetCityForecastByZIP">
<soap12:operation soapAction="http://ws.cdyne.com/WeatherWS/GetCityForecastByZIP"/>
<input>
<soap12:body use="literal"/>
</input>
<output>
<soap12:body use="literal"/>
</output>
</operation>
<operation name="GetCityWeatherByZIP">
<soap12:operation soapAction="http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP"/>
<input>
<soap12:body use="literal"/>
</input>
<output>
<soap12:body use="literal"/>
</output>
</operation>
</binding>
<service name="WeatherSoapStubImplService">
<port name="WeatherSoapPort" binding="tns:WeatherSoapPortBinding">
<soap12:address location="REPLACE_WITH_ACTUAL_URL"/>
</port>
</service>
</definitions>
Create the stub or mock service with tooling such as SOAPUI
With this approach and simple service interface I was able to successfully send SOAP messages with the client to a mock service provider created in SOAPUI created with the generated WSDL.

context.Trigger.JobDataMap values via xml configuration

In quartz_jobs.xml, I can set some parameters for the job.....
<job>
<name>MyJob</name>
<group>MyJob</group>
<description>My Job</description>
<job-type>MyAssembly.MyJob, MyAssembly</job-type>
<durable>true</durable>
<recover>false</recover>
<job-data-map>
<entry>
<key>JobMapDataKeyOne</key>
<value>JobMapDataValueOne</value>
</entry>
<entry>
<key>JobMapDataKeyTwo</key>
<value>JobMapDataValueTwo</value>
</entry>
</job-data-map>
</job>
and here is the code:
public class MyJob: IJob
{
public virtual void Execute(IJobExecutionContext context)
{
JobKey key = context.JobDetail.Key;
JobDataMap jbDataMap = context.JobDetail.JobDataMap;
string jobMapDataValueOne = jbDataMap.GetString("JobMapDataKeyOne");
string jobMapDataValueTwo = jbDataMap.GetString("JobMapDataKeyOne");
}
}
Now, I can "code up a job and trigger" (not using .xml setup) (code not seen).... and I can get the below to work.
(And have populated values for triggerParameter001Value and triggerParameter002Value ).
public class MyJob: IJob
{
public virtual void Execute(IJobExecutionContext context)
{
JobKey key = context.JobDetail.Key;
JobDataMap trgDataMap = context.Trigger.JobDataMap;
string triggerParameter001Value = trgDataMap.GetString("TriggerParameter001Key");
string triggerParameter002Value = trgDataMap.GetString("TriggerParameter002Key");
}
}
But I don't see a way to pass parameters for the Trigger...defined in the xml.
I searched for
"trigger-data-map"
and
"jobtrigger-data-map"
to no avail.
I fished around the "http://quartznet.sourceforge.net/JobSchedulingData" xsd as well.
Is this just missing in the xml?
Am I missing something?
Ok. This one was SNEAKY!
The below will NOT work: (note the position of "job-data-map" element ~under the "simple" element)
<job>
<name>MyJob</name>
<group>MyJobGroup</group>
<description>My Job</description>
<job-type>MyAssembly.MyJob, MyAssembly</job-type>
<durable>true</durable>
<recover>false</recover>
<job-data-map>
<entry>
<key>JobMapDataKeyOne</key>
<value>JobMapDataValueOne</value>
</entry>
<entry>
<key>JobMapDataKeyTwo</key>
<value>JobMapDataValueTwo</value>
</entry>
</job-data-map>
</job>
<trigger>
<simple>
<name>MyTrigger</name>
<group>MyTriggerJobGroup</group>
<description>MyTriggerDescription</description>
<job-name>MyJob</job-name>
<job-group>MyJobGroup</job-group>
<!--<start-time>1982-06-28T18:15:00.0Z</start-time>-->
<!--<end-time>2020-05-04T18:13:51.0Z</end-time>-->
<misfire-instruction>SmartPolicy</misfire-instruction>
<!-- repeat indefinitely every 10 seconds -->
<repeat-count>-1</repeat-count>
<repeat-interval>5000</repeat-interval>
<job-data-map>
<entry>
<key>TriggerParameter001Key</key>
<value>TriggerParameter001Value</value>
</entry>
<entry>
<key>TriggerParameter002Key</key>
<value>TriggerParameter002Value</value>
</entry>
</job-data-map>
</simple>
</trigger>
The above xml was giving me an error like this:
Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin.ProcessFile Error Error scheduling jobs: The element 'simple' in namespace 'http://quartznet.sourceforge.net/JobSchedulingData' has invalid child element 'job-data-map' in namespace 'http://quartznet.sourceforge.net/JobSchedulingData'.
........................
The below WILL work (note the position change of "job-data-map" (still under "simple" element, but moved "up" some)
<job>
<name>MyJob</name>
<group>MyJobGroup</group>
<description>My Job</description>
<job-type>MyAssembly.MyJob, MyAssembly</job-type>
<durable>true</durable>
<recover>false</recover>
<job-data-map>
<entry>
<key>JobMapDataKeyOne</key>
<value>JobMapDataValueOne</value>
</entry>
<entry>
<key>JobMapDataKeyTwo</key>
<value>JobMapDataValueTwo</value>
</entry>
</job-data-map>
</job>
<trigger>
<simple>
<name>MyTrigger</name>
<group>MyTriggerJobGroup</group>
<description>MyTriggerDescription</description>
<job-name>MyJob</job-name>
<job-group>MyJobGroup</job-group>
<job-data-map>
<entry>
<key>TriggerParameter001Key</key>
<value>TriggerParameter001Value</value>
</entry>
<entry>
<key>TriggerParameter002Key</key>
<value>TriggerParameter002Value</value>
</entry>
</job-data-map>
<!--<start-time>1982-06-28T18:15:00.0Z</start-time>-->
<!--<end-time>2020-05-04T18:13:51.0Z</end-time>-->
<misfire-instruction>SmartPolicy</misfire-instruction>
<!-- repeat indefinitely every 10 seconds -->
<repeat-count>-1</repeat-count>
<repeat-interval>5000</repeat-interval>
</simple>
</trigger>
Why?
The xsd uses an abstractType
<xs:complexType name="abstractTriggerType" abstract="true">
<xs:annotation>
<xs:documentation>Common Trigger definitions</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="group" type="xs:string" minOccurs="0" />
<xs:element name="description" type="xs:string" minOccurs="0" />
<xs:element name="job-name" type="xs:string" />
<xs:element name="job-group" type="xs:string" minOccurs="0" />
<xs:element name="priority" type="xs:nonNegativeInteger" minOccurs="0" />
<xs:element name="calendar-name" type="xs:string" minOccurs="0" />
<xs:element name="job-data-map" type="job-data-mapType" minOccurs="0" />
<xs:complexType name="simpleTriggerType">
<xs:annotation>
<xs:documentation>Define a SimpleTrigger</xs:documentation>
</xs:annotation>
<xs:complexContent>
<xs:extension base="abstractTriggerType">
<xs:sequence>
<xs:element name="misfire-instruction" type="simple-trigger-misfire-instructionType" minOccurs="0" />
<xs:sequence minOccurs="0">
<xs:element name="repeat-count" type="repeat-countType" />
<xs:element name="repeat-interval" type="xs:nonNegativeInteger" />
</xs:sequence>
So everything that is a part of the abstract has to be defined ~before~ any of the concrete properties.
That is sneaky!

Return type in wsimport-generated service stub methods

Here is an example of wsimport-generated service stub method:
#WebMethod(operationName = "GetSynonym", action = "GetSynonymRequest")
#WebResult(name = "Synonyms", targetNamespace = "service.bnsf.com/contact/ContactMessages")
#RequestWrapper(localName = "GetSynonym", targetNamespace = "service.bnsf.com/contact/ContactMessages", className = "com.bnsf.service.contact.contactmessages.GetSynonymRequest")
#ResponseWrapper(localName = "GetSynonymResponse", targetNamespace = "service.bnsf.com/contact/ContactMessages", className = "com.bnsf.service.contact.contactmessages.GetSynonymResponse")
public Synonyms getSynonym(
#WebParam(name = "RequestContext", targetNamespace = "service.bnsf.com/contact/ContactMessages") RequestContext requestContext,
#WebParam(name = "SynonymId", targetNamespace = "service.bnsf.com/contact/ContactMessages") EntityId synonymId)
throws BusinessFaultMessage, ServiceFaultMessage;
Note that return type is Synonyms class.
Here are the relevant wsdl parts:
<xs:element name="GetSynonymResponse" type="GetSynonymResponse"/>
<xs:complexType name="GetSynonymResponse">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="1" name="Synonyms" type="account:Synonyms"/>
</xs:sequence>
</xs:complexType>
...
<wsdl:message name="GetSynonymResponse">
<wsdl:part element="msg:GetSynonymResponse" name="GetSynonymResponse"/>
</wsdl:message>
...
<wsdl:operation name="GetSynonym">
<soap:operation soapAction="GetSynonymRequest" style="document"/>
<wsdl:input name="GetSynonymRequestRequest">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="GetSynonymRequestResponse">
<soap:body use="literal"/>
</wsdl:output>
<wsdl:fault name="BusinessFault">
<soap:fault name="BusinessFault" use="literal"/>
</wsdl:fault>
<wsdl:fault name="ServiceFault">
<soap:fault name="ServiceFault" use="literal"/>
</wsdl:fault>
</wsdl:operation>
By default wsimport has generated service method with Synonyms class as return type rather than GetSynonymResponse class.
My question is whether this is customizable - is there a possibility to make wsimport generate service methods with different signatures, particularly having GetSynonymResponse class as return type?
Thanks in advance,
Valery
Found how this is configurable:
The feature called "WrapperStyle" should be disabled to make generated method return xxxResponse type.
This is accomplishable by providing -b parameter to wsimport like
wsimport" -b binding.xml ContactService.wsdl
with binding.xml contents as
<jaxws:bindings wsdlLocation="ContactService.wsdl"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb">
<!-- Turn off wrapper style Java method signature generation -->
<jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
</jaxws:bindings>

How do I get an attribute using the 'amazon-ecs' gem?

I am using Amazon Product Advertising API (amazon-ecs) gem, but I am using the item_lookup method instead of item_search, which is the only one documented.
I am looking to return the item TradeInValue but I am confused as to how to do that. It is clearly being returned, but I am not sure how to parse it.
This is the call:
Amazon::Ecs.item_lookup('9780521153348', :response_group => 'ItemAttributes', :id_type => 'ISBN', :search_index => 'Books')
And this is the return, which I am not sure what to do with. The methods in amazon-ecs don't seem to work for item_lookup and XPath isn't working either:
#<Amazon::Ecs::Response:0x007ff3325250d0 #doc=#<Nokogiri::XML::Document:0x3ff9992927a0 name="document" children=[#<Nokogiri::XML::Element:0x3ff997067414 name="ItemLookupResponse" children=[#<Nokogiri::XML::Element:0x3ff99706b924 name="OperationRequest" children=[#<Nokogiri::XML::Element:0x3ff99706b474 name="HTTPHeaders" children=[#<Nokogiri::XML::Element:0x3ff99706f470 name="Header" attributes=[#<Nokogiri::XML::Attr:0x3ff99706f0ec name="Name" value="UserAgent">, #<Nokogiri::XML::Attr:0x3ff99706f074 name="Value" value="Ruby">]>]>, #<Nokogiri::XML::Element:0x3ff9970760cc name="RequestId" children=[#<Nokogiri::XML::Text:0x3ff999296468 "053626d3-e3cd-47d2-bc8b-7ac6a0c5f6d2">]>, #<Nokogiri::XML::Element:0x3ff99929f6f8 name="Arguments" children=[#<Nokogiri::XML::Element:0x3ff9992a3bb8 name="Argument" attributes=[#<Nokogiri::XML::Attr:0x3ff9992a3960 name="Name" value="Operation">, #<Nokogiri::XML::Attr:0x3ff9992a3938 name="Value" value="ItemLookup">]>, #<Nokogiri::XML::Element:0x3ff9992a73f8 name="Argument" attributes=[#<Nokogiri::XML::Attr:0x3ff9992a7204 name="Name" value="Signature">, #<Nokogiri::XML::Attr:0x3ff9992a7010 name="Value" value="IMd3D0DGgAcaLR6XcuObzdAgFbOya7mbIRtZFbNijVA=">]>, #<Nokogiri::XML::Element:0x3ff9992ab41c name="Argument" attributes=[#<Nokogiri::XML::Attr:0x3ff9992ab2dc name="Name" value="AssociateTag">, #<Nokogiri::XML::Attr:0x3ff9992ab2c8 name="Value" value="textscom-20">]>, #<Nokogiri::XML::Element:0x3ff9992af774 name="Argument" attributes=[#<Nokogiri::XML::Attr:0x3ff9992af6ac name="Name" value="ItemId">, #<Nokogiri::XML::Attr:0x3ff9992af698 name="Value" value="9780521153348">]>, #<Nokogiri::XML::Element:0x3ff9992b0c3c name="Argument" attributes=[#<Nokogiri::XML::Attr:0x3ff9992b0b4c name="Name" value="IdType">, #<Nokogiri::XML::Attr:0x3ff9992b0b10 name="Value" value="ISBN">]>, #<Nokogiri::XML::Element:0x3ff9992b7794 name="Argument" attributes=[#<Nokogiri::XML::Attr:0x3ff9992b7578 name="Name" value="AWSAccessKeyId">, #<Nokogiri::XML::Attr:0x3ff9992b7550 name="Value" value="AKIAJLOCEGWTFXZKXLEQ">]>, #<Nokogiri::XML::Element:0x3ff9992b2244 name="Argument" attributes=[#<Nokogiri::XML::Attr:0x3ff9992b208c name="Name" value="Timestamp">, #<Nokogiri::XML::Attr:0x3ff9992b2064 name="Value" value="2013-05-07T17:46:35Z">]>, #<Nokogiri::XML::Element:0x3ff9992bffc0 name="Argument" attributes=[#<Nokogiri::XML::Attr:0x3ff9992bff20 name="Name" value="ResponseGroup">, #<Nokogiri::XML::Attr:0x3ff9992bff0c name="Value" value="ItemAttributes">]>, #<Nokogiri::XML::Element:0x3ff9992be1e8 name="Argument" attributes=[#<Nokogiri::XML::Attr:0x3ff9992c38c8 name="Name" value="SearchIndex">, #<Nokogiri::XML::Attr:0x3ff9992c3a30 name="Value" value="Books">]>, #<Nokogiri::XML::Element:0x3ff9992ca95c name="Argument" attributes=[#<Nokogiri::XML::Attr:0x3ff9992ca344 name="Name" value="Service">, #<Nokogiri::XML::Attr:0x3ff9992ca2a4 name="Value" value="AWSECommerceService">]>]>, #<Nokogiri::XML::Element:0x3ff9992d3d7c name="RequestProcessingTime" children=[#<Nokogiri::XML::Text:0x3ff9992d382c "0.0289960000000000">]>]>, #<Nokogiri::XML::Element:0x3ff9992d7eb8 name="Items" children=[#<Nokogiri::XML::Element:0x3ff9992d7148 name="Request" children=[#<Nokogiri::XML::Element:0x3ff9992d6900 name="IsValid" children=[#<Nokogiri::XML::Text:0x3ff9992dae10 "True">]>, #<Nokogiri::XML::Element:0x3ff9992da758 name="ItemLookupRequest" children=[#<Nokogiri::XML::Element:0x3ff9992df884 name="IdType" children=[#<Nokogiri::XML::Text:0x3ff9992df014 "ISBN">]>, #<Nokogiri::XML::Element:0x3ff9992de678 name="ItemId" children=[#<Nokogiri::XML::Text:0x3ff9992e2e1c "9780521153348">]>, #<Nokogiri::XML::Element:0x3ff9983a5580 name="ResponseGroup" children=[#<Nokogiri::XML::Text:0x3ff9983a59b8 "ItemAttributes">]>, #<Nokogiri::XML::Element:0x3ff9983a510c name="SearchIndex" children=[#<Nokogiri::XML::Text:0x3ff9983a43c4 "Books">]>, #<Nokogiri::XML::Element:0x3ff9983a925c name="VariationPage" children=[#<Nokogiri::XML::Text:0x3ff9983a8d0c "All">]>]>]>, #<Nokogiri::XML::Element:0x3ff9983a8244 name="Item" children=[#<Nokogiri::XML::Element:0x3ff9983add70 name="ASIN" children=[#<Nokogiri::XML::Text:0x3ff9983ad6f4 "0521153344">]>, #<Nokogiri::XML::Element:0x3ff9983acc04 name="DetailPageURL" children=[#<Nokogiri::XML::Text:0x3ff9983ac4d4 "http://www.amazon.com/Turgot-Progress-Sociology-Economics-Philosophical/dp/0521153344%3FSubscriptionId%3DAKIAJLOCEGWTFXZKXLEQ%26tag%3Dtextscom-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0521153344">]>, #<Nokogiri::XML::Element:0x3ff9983b1768 name="ItemLinks" children=[#<Nokogiri::XML::Element:0x3ff9983b1024 name="ItemLink" children=[#<Nokogiri::XML::Element:0x3ff9983b0a20 name="Description" children=[#<Nokogiri::XML::Text:0x3ff9983b028c "Technical Details">]>, #<Nokogiri::XML::Element:0x3ff9983b5b60 name="URL" children=[#<Nokogiri::XML::Text:0x3ff9983b5638 "http://www.amazon.com/Turgot-Progress-Sociology-Economics-Philosophical/dp/tech-data/0521153344%3FSubscriptionId%3DAKIAJLOCEGWTFXZKXLEQ%26tag%3Dtextscom-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0521153344">]>]>, #<Nokogiri::XML::Element:0x3ff9983b5034 name="ItemLink" children=[#<Nokogiri::XML::Element:0x3ff9983b4a58 name="Description" children=[#<Nokogiri::XML::Text:0x3ff9983b46d4 "Add To Baby Registry">]>, #<Nokogiri::XML::Element:0x3ff9983b406c name="URL" children=[#<Nokogiri::XML::Text:0x3ff9983b9ad0 "http://www.amazon.com/gp/registry/baby/add-item.html%3Fasin.0%3D0521153344%26SubscriptionId%3DAKIAJLOCEGWTFXZKXLEQ%26tag%3Dtextscom-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0521153344">]>]>, #<Nokogiri::XML::Element:0x3ff9983b95d0 name="ItemLink" children=[#<Nokogiri::XML::Element:0x3ff9983b9080 name="Description" children=[#<Nokogiri::XML::Text:0x3ff9983b8cd4 "Add To Wedding Registry">]>, #<Nokogiri::XML::Element:0x3ff9983b8a2c name="URL" children=[#<Nokogiri::XML::Text:0x3ff9983b834c "http://www.amazon.com/gp/registry/wedding/add-item.html%3Fasin.0%3D0521153344%26SubscriptionId%3DAKIAJLOCEGWTFXZKXLEQ%26tag%3Dtextscom-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0521153344">]>]>, #<Nokogiri::XML::Element:0x3ff9983bcff0 name="ItemLink" children=[#<Nokogiri::XML::Element:0x3ff9983bc49c name="Description" children=[#<Nokogiri::XML::Text:0x3ff9983bc0f0 "Add To Wishlist">]>, #<Nokogiri::XML::Element:0x3ff9983c1b40 name="URL" children=[#<Nokogiri::XML::Text:0x3ff9983c158c "http://www.amazon.com/gp/registry/wishlist/add-item.html%3Fasin.0%3D0521153344%26SubscriptionId%3DAKIAJLOCEGWTFXZKXLEQ%26tag%3Dtextscom-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0521153344">]>]>, #<Nokogiri::XML::Element:0x3ff9983c01dc name="ItemLink" children=[#<Nokogiri::XML::Element:0x3ff9983c5da8 name="Description" children=[#<Nokogiri::XML::Text:0x3ff9983c595c "Tell A Friend">]>, #<Nokogiri::XML::Element:0x3ff9983c5358 name="URL" children=[#<Nokogiri::XML::Text:0x3ff9983c4674 "http://www.amazon.com/gp/pdp/taf/0521153344%3FSubscriptionId%3DAKIAJLOCEGWTFXZKXLEQ%26tag%3Dtextscom-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0521153344">]>]>, #<Nokogiri::XML::Element:0x3ff9983c9750 name="ItemLink" children=[#<Nokogiri::XML::Element:0x3ff9983c8828 name="Description" children=[#<Nokogiri::XML::Text:0x3ff9983cc888 "All Customer Reviews">]>, #<Nokogiri::XML::Element:0x3ff9983d1f18 name="URL" children=[#<Nokogiri::XML::Text:0x3ff9983d19c8 "http://www.amazon.com/review/product/0521153344%3FSubscriptionId%3DAKIAJLOCEGWTFXZKXLEQ%26tag%3Dtextscom-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0521153344">]>]>, #<Nokogiri::XML::Element:0x3ff9983d152c name="ItemLink" children=[#<Nokogiri::XML::Element:0x3ff9983d107c name="Description" children=[#<Nokogiri::XML::Text:0x3ff9983d02d0 "All Offers">]>, #<Nokogiri::XML::Element:0x3ff9983d5730 name="URL" children=[#<Nokogiri::XML::Text:0x3ff9983d4e48 "http://www.amazon.com/gp/offer-listing/0521153344%3FSubscriptionId%3DAKIAJLOCEGWTFXZKXLEQ%26tag%3Dtextscom-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0521153344">]>]>]>, #<Nokogiri::XML::Element:0x3ff9983dd00c name="ItemAttributes" children=[#<Nokogiri::XML::Element:0x3ff9983dc33c name="Author" children=[#<Nokogiri::XML::Text:0x3ff9983e1364 "Ronald L. Meek">]>, #<Nokogiri::XML::Element:0x3ff9983e0af4 name="Binding" children=[#<Nokogiri::XML::Text:0x3ff9983e04a0 "Paperback">]>, #<Nokogiri::XML::Element:0x3ff9983e0068 name="EAN" children=[#<Nokogiri::XML::Text:0x3ff9983e5c34 "9780521153348">]>, #<Nokogiri::XML::Element:0x3ff9983e5270 name="EANList" children=[#<Nokogiri::XML::Element:0x3ff9983e4424 name="EANListElement" children=[#<Nokogiri::XML::Text:0x3ff9983d7c60 "9780521153348">]>]>, #<Nokogiri::XML::Element:0x3ff9983d7724 name="ISBN" children=[#<Nokogiri::XML::Text:0x3ff9983d7120 "0521153344">]>, #<Nokogiri::XML::Element:0x3ff9983d6cc0 name="IsEligibleForTradeIn" children=[#<Nokogiri::XML::Text:0x3ff9983d6130 "1">]>, #<Nokogiri::XML::Element:0x3ff9983d99c0 name="ItemDimensions" children=[#<Nokogiri::XML::Element:0x3ff9983d963c name="Height" attributes=[#<Nokogiri::XML::Attr:0x3ff9983d959c name="Units" value="hundredths-inches">] children=[#<Nokogiri::XML::Text:0x3ff9983d83cc "902">]>, #<Nokogiri::XML::Element:0x3ff9983ed7a4 name="Length" attributes=[#<Nokogiri::XML::Attr:0x3ff9983ed448 name="Units" value="hundredths-inches">] children=[#<Nokogiri::XML::Text:0x3ff9983f3244 "598">]>, #<Nokogiri::XML::Element:0x3ff9983f290c name="Weight" attributes=[#<Nokogiri::XML::Attr:0x3ff9983f26f0 name="Units" value="hundredths-pounds">] children=[#<Nokogiri::XML::Text:0x3ff9983e93c0 "64">]>, #<Nokogiri::XML::Element:0x3ff9983e8f38 name="Width" attributes=[#<Nokogiri::XML::Attr:0x3ff9983e8e34 name="Units" value="hundredths-inches">] children=[#<Nokogiri::XML::Text:0x3ff9983efa2c "43">]>]>, #<Nokogiri::XML::Element:0x3ff9983ef144 name="Label" children=[#<Nokogiri::XML::Text:0x3ff9983eecbc "Cambridge University Press">]>, #<Nokogiri::XML::Element:0x3ff9983ee988 name="Languages" children=[#<Nokogiri::XML::Element:0x3ff9983ee410 name="Language" children=[#<Nokogiri::XML::Element:0x3ff99a80009c name="Name" children=[#<Nokogiri::XML::Text:0x3ff99a801a14 "English">]>, #<Nokogiri::XML::Element:0x3ff99a8010f0 name="Type" children=[#<Nokogiri::XML::Text:0x3ff99a800948 "Unknown">]>]>, #<Nokogiri::XML::Element:0x3ff99a800574 name="Language" children=[#<Nokogiri::XML::Element:0x3ff99a8002b8 name="Name" children=[#<Nokogiri::XML::Text:0x3ff99a80419c "English">]>, #<Nokogiri::XML::Element:0x3ff99a805484 name="Type" children=[#<Nokogiri::XML::Text:0x3ff99a804b10 "Original Language">]>]>, #<Nokogiri::XML::Element:0x3ff99a804070 name="Language" children=[#<Nokogiri::XML::Element:0x3ff99a808d64 name="Name" children=[#<Nokogiri::XML::Text:0x3ff99a80d6d4 "English">]>, #<Nokogiri::XML::Element:0x3ff99a80c680 name="Type" children=[#<Nokogiri::XML::Text:0x3ff99a8110a4 "Published">]>]>]>, #<Nokogiri::XML::Element:0x3ff99a8157a8 name="ListPrice" children=[#<Nokogiri::XML::Element:0x3ff99a8142cc name="Amount" children=[#<Nokogiri::XML::Text:0x3ff99a819358 "2899">]>, #<Nokogiri::XML::Element:0x3ff99a818d04 name="CurrencyCode" children=[#<Nokogiri::XML::Text:0x3ff99a818764 "USD">]>, #<Nokogiri::XML::Element:0x3ff99a81c8a0 name="FormattedPrice" children=[#<Nokogiri::XML::Text:0x3ff99a81d534 "$28.99">]>]>, #<Nokogiri::XML::Element:0x3ff99a81ccb0 name="Manufacturer" children=[#<Nokogiri::XML::Text:0x3ff99a81c648 "Cambridge University Press">]>, #<Nokogiri::XML::Element:0x3ff99a81c2d8 name="NumberOfItems" children=[#<Nokogiri::XML::Text:0x3ff99a821cec "1">]>, #<Nokogiri::XML::Element:0x3ff99a821580 name="NumberOfPages" children=[#<Nokogiri::XML::Text:0x3ff99a82043c "194">]>, #<Nokogiri::XML::Element:0x3ff99a825bbc name="PackageDimensions" children=[#<Nokogiri::XML::Element:0x3ff99a824cd0 name="Height" attributes=[#<Nokogiri::XML::Attr:0x3ff99a824b68 name="Units" value="hundredths-inches">] children=[#<Nokogiri::XML::Text:0x3ff99a824168 "63">]>, #<Nokogiri::XML::Element:0x3ff99a829d98 name="Length" attributes=[#<Nokogiri::XML::Attr:0x3ff99a829d20 name="Units" value="hundredths-inches">] children=[#<Nokogiri::XML::Text:0x3ff99a828eac "890">]>, #<Nokogiri::XML::Element:0x3ff99a828c68 name="Weight" attributes=[#<Nokogiri::XML::Attr:0x3ff99a828b8c name="Units" value="hundredths-pounds">] children=[#<Nokogiri::XML::Text:0x3ff99a828074 "66">]>, #<Nokogiri::XML::Element:0x3ff9983f69d0 name="Width" attributes=[#<Nokogiri::XML::Attr:0x3ff9983f68f4 name="Units" value="hundredths-inches">] children=[#<Nokogiri::XML::Text:0x3ff997c6340c "598">]>]>, #<Nokogiri::XML::Element:0x3ff997c62cf0 name="ProductGroup" children=[#<Nokogiri::XML::Text:0x3ff997c623b8 "Book">]>, #<Nokogiri::XML::Element:0x3ff9961e1d2c name="ProductTypeName" children=[#<Nokogiri::XML::Text:0x3ff9961e0e40 "ABIS_BOOK">]>, #<Nokogiri::XML::Element:0x3ff9961e0508 name="PublicationDate" children=[#<Nokogiri::XML::Text:0x3ff9961e7b28 "2010-06-10">]>, #<Nokogiri::XML::Element:0x3ff9961e75c4 name="Publisher" children=[#<Nokogiri::XML::Text:0x3ff9961e727c "Cambridge University Press">]>, #<Nokogiri::XML::Element:0x3ff9961e70b0 name="SKU" children=[#<Nokogiri::XML::Text:0x3ff9961e6bc4 "Y9780521153348">]>, #<Nokogiri::XML::Element:0x3ff9961e682c name="Studio" children=[#<Nokogiri::XML::Text:0x3ff9961ebc64 "Cambridge University Press">]>, #<Nokogiri::XML::Element:0x3ff9961ea120 name="Title" children=[#<Nokogiri::XML::Text:0x3ff996f67ac8 "Turgot on Progress, Sociology and Economics: A Philosophical Review of the Successive Advances of the Human Mind on Universal History Reflections on ... in the History and Theory of Politics)">]>, #<Nokogiri::XML::Element:0x3ff996f676cc name="TradeInValue" children=[#<Nokogiri::XML::Element:0x3ff996f67028 name="Amount" children=[#<Nokogiri::XML::Text:0x3ff996f66ca4 "310">]>, #<Nokogiri::XML::Element:0x3ff996f669d4 name="CurrencyCode" children=[#<Nokogiri::XML::Text:0x3ff996f663a8 "USD">]>, #<Nokogiri::XML::Element:0x3ff996fadbb8 name="FormattedPrice" children=[#<Nokogiri::XML::Text:0x3ff996fad014 "$3.10">]>]>]>]>]>]>]>>
Okay, I figured it out. I was trying to treat item_lookup as a single return value, rather than a hash the way search is returned.
item_lookup is basically the same as item_search:
res = Amazon::Ecs.item_lookup('9780521153348', :response_group => 'ItemAttributes', :id_type => 'ISBN', :search_index => 'Books')
res.items.each do |item|
puts item.get('ItemAttributes/TradeInValue/FormattedPrice')
end

Resources