lolsoap 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -36,7 +36,9 @@ client = LolSoap::Client.new(File.read('lolapi.wsdl'))
36
36
  request = client.request('getLols')
37
37
 
38
38
  # Populate the request with some data. Namespacing is taken care of
39
- # using the type data from the WSDL.
39
+ # using the type data from the WSDL. The WSDL also tells us whether
40
+ # a given name (e.g. lolFactor below) should be treated as an
41
+ # attribute or a sub-element.
40
42
  request.body do |b|
41
43
  b.lolFactor '11'
42
44
  b.lolDuration 'lolever'
@@ -65,10 +67,11 @@ p response.body_hash
65
67
 
66
68
  ## Bugs/Features ##
67
69
 
68
- * SOAP 1.1 is not supported. Patches to add support will be considered
69
- if they don't add too much extra complexity.
70
70
  * WSSE is not supported.
71
71
  * Assumes that you are able to supply a WSDL document for the service.
72
+ * Some of the finer details of namespace handling may be glossed over.
73
+ This is just pragmatism; patches to improve namespace handling are
74
+ welcome.
72
75
 
73
76
  ## Overview ##
74
77
 
@@ -100,6 +103,24 @@ The others:
100
103
 
101
104
  Development sponsored by [Loco2](http://loco2.com/).
102
105
 
106
+ ## Changelog ##
107
+
108
+ ### 0.2 ###
109
+
110
+ * SOAP 1.1 support
111
+ * Better handling of namespaces in the XML schema. You should now be
112
+ able to have two types a:foo and b:foo and it will Just Work. This
113
+ introduces some API incompatibilities with version 1.1. Specifically,
114
+ `LolSoap::WSDL#types` is now keyed based on a prefixed type name
115
+ rather than an unprefixed type name. This shouldn't affect you if
116
+ you're not using `LolSoap::WSDL#types` directly.
117
+ * Add support for building attributes with `LolSoap::Builder` based on
118
+ the XML Schema information.
119
+
120
+ ### 0.1 ###
121
+
122
+ Initial release
123
+
103
124
  ## License ##
104
125
 
105
126
  (The MIT License)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.4
1
+ 0.2.0
@@ -55,6 +55,10 @@ module LolSoap
55
55
  __prefixed_tag__(@type.prefix, @type.sub_type(name.to_s), name, *args, &block)
56
56
  end
57
57
 
58
+ def __attribute__(name, value)
59
+ @node[name.to_s] = value.to_s
60
+ end
61
+
58
62
  # @private
59
63
  def __prefixed_tag__(prefix, sub_type, name, *args)
60
64
  sub_node = @node.document.create_element(name.to_s, *args)
@@ -88,6 +92,13 @@ module LolSoap
88
92
 
89
93
  private
90
94
 
91
- alias method_missing __tag__
95
+ # alias method_missing __tag__
96
+ def method_missing(name, *args, &block)
97
+ if @type.has_attribute?(name.to_s)
98
+ __attribute__(name, *args)
99
+ else
100
+ __tag__(name, *args, &block)
101
+ end
102
+ end
92
103
  end
93
104
  end
@@ -6,10 +6,10 @@ module LolSoap
6
6
  attr_reader :wsdl, :operation, :doc
7
7
 
8
8
  # @private
9
- SOAP_PREFIX = 'soap'
9
+ SOAP_1_1 = 'http://schemas.xmlsoap.org/soap/envelope/'
10
10
 
11
11
  # @private
12
- SOAP_NAMESPACE = 'http://www.w3.org/2003/05/soap-envelope'
12
+ SOAP_1_2 = 'http://www.w3.org/2003/05/soap-envelope'
13
13
 
14
14
  def initialize(wsdl, operation, doc = Nokogiri::XML::Document.new)
15
15
  @wsdl = wsdl
@@ -54,16 +54,20 @@ module LolSoap
54
54
  operation.output
55
55
  end
56
56
 
57
- def to_xml
58
- doc.to_xml
57
+ def to_xml(options = {})
58
+ doc.to_xml(options)
59
59
  end
60
60
 
61
61
  def soap_prefix
62
- SOAP_PREFIX
62
+ 'soap'
63
63
  end
64
64
 
65
65
  def soap_namespace
66
- SOAP_NAMESPACE
66
+ soap_version == '1.2' ? SOAP_1_2 : SOAP_1_1
67
+ end
68
+
69
+ def soap_version
70
+ wsdl.soap_version
67
71
  end
68
72
 
69
73
  private
data/lib/lolsoap/fault.rb CHANGED
@@ -11,16 +11,29 @@ module LolSoap
11
11
  request.soap_namespace
12
12
  end
13
13
 
14
+ def soap_version
15
+ request.soap_version
16
+ end
17
+
14
18
  def code
15
- node.at_xpath('./soap:Code/soap:Value', 'soap' => soap_namespace).text.to_s
19
+ node.at_xpath(
20
+ soap_version == '1.2' ? './soap:Code/soap:Value' : './soap:faultcode',
21
+ 'soap' => soap_namespace
22
+ ).text.to_s
16
23
  end
17
24
 
18
25
  def reason
19
- node.at_xpath('./soap:Reason/soap:Text', 'soap' => soap_namespace).text.to_s
26
+ node.at_xpath(
27
+ soap_version == '1.2' ? './soap:Reason/soap:Text' : './soap:faultstring',
28
+ 'soap' => soap_namespace
29
+ ).text.to_s
20
30
  end
21
31
 
22
32
  def detail
23
- node.at_xpath('./soap:Detail/*', 'soap' => soap_namespace).to_xml
33
+ node.at_xpath(
34
+ soap_version == '1.2' ? './soap:Detail/*' : './soap:detail/*',
35
+ 'soap' => soap_namespace
36
+ ).to_xml
24
37
  end
25
38
  end
26
39
  end
@@ -1,10 +1,12 @@
1
1
  module LolSoap
2
2
  # Represents a HTTP request containing a SOAP Envelope
3
3
  class Request
4
- attr_reader :envelope
4
+ attr_reader :envelope
5
+ attr_accessor :xml_options
5
6
 
6
7
  def initialize(envelope)
7
- @envelope = envelope
8
+ @envelope = envelope
9
+ @xml_options = {}
8
10
  end
9
11
 
10
12
  # @see Envelope#body
@@ -22,6 +24,11 @@ module LolSoap
22
24
  envelope.soap_namespace
23
25
  end
24
26
 
27
+ # The SOAP version in use
28
+ def soap_version
29
+ envelope.soap_version
30
+ end
31
+
25
32
  # URL to be POSTed to
26
33
  def url
27
34
  envelope.endpoint
@@ -40,7 +47,11 @@ module LolSoap
40
47
  # The MIME type of the request. This is always application/soap+xml,
41
48
  # but it could be overridden in a subclass.
42
49
  def mime
43
- 'application/soap+xml'
50
+ if soap_version == '1.1'
51
+ 'text/xml'
52
+ else
53
+ 'application/soap+xml'
54
+ end
44
55
  end
45
56
 
46
57
  # The charset of the request. This is always UTF-8, but it could be
@@ -66,7 +77,7 @@ module LolSoap
66
77
 
67
78
  # The content to be sent in the HTTP request
68
79
  def content
69
- @content ||= envelope.to_xml
80
+ @content ||= envelope.to_xml(xml_options)
70
81
  end
71
82
  end
72
83
  end
@@ -9,14 +9,14 @@ class LolSoap::WSDL
9
9
  @singular = singular
10
10
  end
11
11
 
12
- def type
13
- @type ||= wsdl.type(@type_name.split(':').last)
14
- end
15
-
16
12
  def singular?
17
13
  @singular == true
18
14
  end
19
15
 
16
+ def type
17
+ @type ||= wsdl.type(@type_name)
18
+ end
19
+
20
20
  def inspect
21
21
  "<#{self.class} name=#{name.inspect} type=#{@type_name.inspect}>"
22
22
  end
@@ -1,12 +1,14 @@
1
+ require 'set'
2
+
1
3
  class LolSoap::WSDL
2
4
  class Type
3
- attr_reader :name, :namespace
5
+ attr_reader :name, :prefix
4
6
 
5
- def initialize(wsdl, name, namespace, elements)
6
- @wsdl = wsdl
7
- @name = name
8
- @namespace = namespace
9
- @elements = elements
7
+ def initialize(name, prefix, elements, attributes)
8
+ @name = name
9
+ @prefix = prefix
10
+ @elements = elements
11
+ @attributes = Set.new(attributes)
10
12
  end
11
13
 
12
14
  def elements
@@ -21,18 +23,18 @@ class LolSoap::WSDL
21
23
  element(name).type
22
24
  end
23
25
 
24
- def prefix
25
- wsdl.prefixes[namespace]
26
+ def attributes
27
+ @attributes.to_a
26
28
  end
27
29
 
28
- def inspect
29
- "<#{self.class} " \
30
- "name=#{(prefix + ':' + name).inspect} " \
31
- "elements=#{elements.inspect}>"
30
+ def has_attribute?(name)
31
+ @attributes.include?(name)
32
32
  end
33
33
 
34
- private
35
-
36
- def wsdl; @wsdl; end
34
+ def inspect
35
+ "<#{self.class} name=\"#{prefix}:#{name}\" " \
36
+ "elements=#{elements.inspect} " \
37
+ "attributes=#{attributes.inspect}>"
38
+ end
37
39
  end
38
40
  end
data/lib/lolsoap/wsdl.rb CHANGED
@@ -13,75 +13,83 @@ module LolSoap
13
13
  new(WSDLParser.parse(raw))
14
14
  end
15
15
 
16
- attr_reader :parser
16
+ # The SOAP endpoint URL
17
+ attr_reader :endpoint
18
+
19
+ # Hash of namespaces used in the WSDL document (keys are prefixes)
20
+ attr_reader :namespaces
21
+
22
+ # Hash of namespace prefixes used in the WSDL document (keys are namespace URIs)
23
+ attr_reader :prefixes
24
+
25
+ # The version of SOAP detected.
26
+ attr_reader :soap_version
17
27
 
18
28
  def initialize(parser)
19
- @parser = parser
29
+ @types = load_types(parser)
30
+ @operations = load_operations(parser)
31
+ @endpoint = parser.endpoint
32
+ @namespaces = parser.namespaces
33
+ @prefixes = parser.prefixes
34
+ @soap_version = parser.soap_version
20
35
  end
21
36
 
22
37
  # Hash of operations that are supports by the SOAP service
23
38
  def operations
24
- load_operations.dup
39
+ @operations.dup
25
40
  end
26
41
 
27
42
  # Get a single operation
28
43
  def operation(name)
29
- load_operations[name]
44
+ @operations.fetch(name)
30
45
  end
31
46
 
32
47
  # Hash of types declared by the service
33
48
  def types
34
- load_types.dup
49
+ @types.dup
35
50
  end
36
51
 
37
52
  # Get a single type, or a NullType if the type doesn't exist
38
53
  def type(name)
39
- load_types.fetch(name) { NullType.new }
40
- end
41
-
42
- # The SOAP endpoint URL
43
- def endpoint
44
- parser.endpoint
45
- end
46
-
47
- # Hash of namespaces used in the WSDL document (keys are prefixes)
48
- def namespaces
49
- parser.namespaces
50
- end
51
-
52
- # Hash of namespace prefixes used in the WSDL document (keys are namespace URIs)
53
- def prefixes
54
- namespaces.invert
54
+ @types.fetch(name) { NullType.new }
55
55
  end
56
56
 
57
57
  # Namespaces used by the types (a subset of #namespaces)
58
58
  def type_namespaces
59
- Hash[parser.types.map { |k, t| [prefixes[t[:namespace]], t[:namespace]] }]
59
+ Hash[@types.values.map { |type| [type.prefix, namespaces[type.prefix]] }]
60
60
  end
61
61
 
62
62
  def inspect
63
63
  "<#{self.class} " \
64
- "namespaces=#{namespaces.inspect} " \
65
- "operations=#{operations.keys.inspect} " \
66
- "types=#{types.keys.inspect}>"
64
+ "namespaces=#{@namespaces.inspect} " \
65
+ "operations=#{@operations.keys.inspect} " \
66
+ "types=#{@types.keys.inspect}>"
67
67
  end
68
68
 
69
69
  private
70
70
 
71
71
  # @private
72
- def load_operations
73
- @operations ||= Hash[
72
+ def load_operations(parser)
73
+ Hash[
74
74
  parser.operations.map do |k, op|
75
- [k, Operation.new(self, op[:action], type(op[:input][:name]), type(op[:output][:name]))]
75
+ [k, Operation.new(self, op[:action], type(op[:input]), type(op[:output]))]
76
76
  end
77
77
  ]
78
78
  end
79
79
 
80
80
  # @private
81
- def load_types
82
- @types ||= Hash[
83
- parser.types.map do |name, type|
84
- [name, Type.new(self, name, type[:namespace], build_elements(type[:elements]))]
81
+ def load_types(parser)
82
+ Hash[
83
+ parser.types.map do |prefixed_name, type|
84
+ [
85
+ prefixed_name,
86
+ Type.new(
87
+ type[:name],
88
+ type[:prefix],
89
+ build_elements(type[:elements]),
90
+ type[:attributes]
91
+ )
92
+ ]
85
93
  end
86
94
  ]
87
95
  end
@@ -3,11 +3,47 @@ require 'nokogiri'
3
3
  module LolSoap
4
4
  # @private
5
5
  class WSDLParser
6
- NS = {
7
- :wsdl => 'http://schemas.xmlsoap.org/wsdl/',
8
- :soap => 'http://schemas.xmlsoap.org/wsdl/soap12/',
9
- :xmlschema => 'http://www.w3.org/2001/XMLSchema'
10
- }
6
+ class Type
7
+ attr_reader :parser, :node, :target_namespace, :name, :prefix
8
+
9
+ def initialize(parser, node, target_namespace)
10
+ @parser = parser
11
+ @node = node
12
+ @target_namespace = target_namespace
13
+ @prefix, @name = prefix_and_name(node.attr('name'))
14
+ end
15
+
16
+ def name_with_prefix
17
+ "#{prefix}:#{name}"
18
+ end
19
+
20
+ def elements
21
+ Hash[
22
+ node.xpath('.//xs:element', parser.ns).map do |element|
23
+ max_occurs = element.attribute('maxOccurs').to_s
24
+
25
+ [
26
+ prefix_and_name(element.attr('name')).last,
27
+ {
28
+ :type => prefix_and_name(element.attr('type')).join(':'),
29
+ :singular => max_occurs.empty? || max_occurs == '1'
30
+ }
31
+ ]
32
+ end
33
+ ]
34
+ end
35
+
36
+ def attributes
37
+ node.xpath('.//xs:attribute/@name', parser.ns).map(&:text)
38
+ end
39
+
40
+ def prefix_and_name(string)
41
+ parser.prefix_and_name(string, target_namespace)
42
+ end
43
+ end
44
+
45
+ SOAP_1_1 = 'http://schemas.xmlsoap.org/wsdl/soap/'
46
+ SOAP_1_2 = 'http://schemas.xmlsoap.org/wsdl/soap12/'
11
47
 
12
48
  attr_reader :doc
13
49
 
@@ -27,79 +63,76 @@ module LolSoap
27
63
  end
28
64
  end
29
65
 
66
+ # We invert the hash in a deterministic way so that the results are repeatable.
67
+ def prefixes
68
+ @prefixes ||= Hash[namespaces.sort_by { |k, v| k }.uniq { |k, v| v }].invert
69
+ end
70
+
30
71
  def endpoint
31
- @endpoint ||= doc.at_xpath(
32
- '/d:definitions/d:service/d:port/soap:address/@location',
33
- 'd' => NS[:wsdl], 'soap' => NS[:soap]
34
- ).to_s
72
+ @endpoint ||= doc.at_xpath('/d:definitions/d:service/d:port/s:address/@location', ns).to_s
73
+ end
74
+
75
+ def schemas
76
+ doc.xpath('/d:definitions/d:types/xs:schema', ns)
35
77
  end
36
78
 
37
79
  def types
38
80
  @types ||= begin
39
- types = doc.xpath(
40
- '/d:definitions/d:types/s:schema/s:element[@name]',
41
- '/d:definitions/d:types/s:schema/s:complexType[@name]',
42
- 'd' => NS[:wsdl], 's' => NS[:xmlschema]
43
- )
44
- Hash[
45
- types.map do |type|
46
- namespace = type.at_xpath('ancestor::s:schema/@targetNamespace', 's' => NS[:xmlschema]).to_s
47
- elements = type.xpath('.//s:element', 's' => NS[:xmlschema])
48
- name = type.attribute('name').to_s
49
-
50
- [
51
- name,
52
- {
53
- :name => name,
54
- :namespace => namespace,
55
- :elements => Hash[elements.map { |e| [e.attribute('name').to_s, element_hash(e)] }]
56
- }
57
- ]
81
+ types = {}
82
+ schemas.each do |schema|
83
+ target_namespace = schema.attr('targetNamespace').to_s
84
+
85
+ schema.xpath('xs:element[@name] | xs:complexType[@name]', ns).each do |node|
86
+ type = Type.new(self, node, target_namespace)
87
+
88
+ types[type.name_with_prefix] = {
89
+ :name => type.name,
90
+ :prefix => type.prefix,
91
+ :elements => type.elements,
92
+ :attributes => type.attributes
93
+ }
58
94
  end
59
- ]
95
+ end
96
+ types
60
97
  end
61
98
  end
62
99
 
63
100
  def messages
64
101
  @messages ||= Hash[
65
- doc.xpath('/d:definitions/d:message', 'd' => NS[:wsdl]).map do |msg|
66
- element = msg.at_xpath('./d:part/@element', 'd' => NS[:wsdl]).to_s
67
- [msg.attribute('name').to_s, types[element.split(':').last]]
102
+ doc.xpath('/d:definitions/d:message', ns).map do |msg|
103
+ element = msg.at_xpath('./d:part/@element', ns).to_s
104
+ [msg.attribute('name').to_s, prefix_and_name(element).join(':')]
68
105
  end
69
106
  ]
70
107
  end
71
108
 
72
109
  def port_type_operations
73
110
  @port_type_operations ||= Hash[
74
- doc.xpath('/d:definitions/d:portType/d:operation', 'd' => NS[:wsdl]).map do |op|
75
- input = op.at_xpath('./d:input/@message', 'd' => NS[:wsdl]).to_s.split(':').last
76
- output = op.at_xpath('./d:output/@message', 'd' => NS[:wsdl]).to_s.split(':').last
111
+ doc.xpath('/d:definitions/d:portType/d:operation', ns).map do |op|
112
+ input = op.at_xpath('./d:input/@message', ns).to_s.split(':').last
113
+ output = op.at_xpath('./d:output/@message', ns).to_s.split(':').last
77
114
  name = op.attribute('name').to_s
78
115
 
79
- [name, { :name => name, :input => messages[input], :output => messages[output] }]
116
+ [name, { :input => messages.fetch(input), :output => messages.fetch(output) }]
80
117
  end
81
118
  ]
82
119
  end
83
120
 
84
121
  def operations
85
122
  @operations ||= begin
86
- binding = doc.at_xpath(
87
- '/d:definitions/d:service/d:port/soap:address/../@binding',
88
- 'd' => NS[:wsdl], 'soap' => NS[:soap]
89
- ).to_s.split(':').last
123
+ binding = doc.at_xpath('/d:definitions/d:service/d:port/s:address/../@binding', ns).to_s.split(':').last
90
124
 
91
125
  Hash[
92
- doc.xpath("/d:definitions/d:binding[@name='#{binding}']/d:operation", 'd' => NS[:wsdl]).map do |op|
126
+ doc.xpath("/d:definitions/d:binding[@name='#{binding}']/d:operation", ns).map do |op|
93
127
  name = op.attribute('name').to_s
94
- action = op.at_xpath('./soap:operation/@soapAction', 'soap' => NS[:soap]).to_s
128
+ action = op.at_xpath('./s:operation/@soapAction', ns).to_s
95
129
 
96
130
  [
97
131
  name,
98
132
  {
99
- :name => name,
100
133
  :action => action,
101
- :input => port_type_operations[name][:input],
102
- :output => port_type_operations[name][:output]
134
+ :input => port_type_operations.fetch(name)[:input],
135
+ :output => port_type_operations.fetch(name)[:output]
103
136
  }
104
137
  ]
105
138
  end
@@ -107,15 +140,30 @@ module LolSoap
107
140
  end
108
141
  end
109
142
 
110
- private
143
+ def soap_version
144
+ @soap_version ||= namespaces.values.include?(SOAP_1_2) ? '1.2' : '1.1'
145
+ end
111
146
 
112
- def element_hash(el)
113
- max_occurs = el.attribute('maxOccurs').to_s
114
- {
115
- :name => el.attribute('name').to_s,
116
- :type => el.attribute('type').to_s,
117
- :singular => max_occurs.empty? || max_occurs == '1'
147
+ def ns
148
+ @ns ||= {
149
+ 'd' => 'http://schemas.xmlsoap.org/wsdl/',
150
+ 'xs' => 'http://www.w3.org/2001/XMLSchema',
151
+ 's' => soap_version == '1.2' ? SOAP_1_2 : SOAP_1_1
118
152
  }
119
153
  end
154
+
155
+ def prefix_and_name(prefixed_name, default_namespace = nil)
156
+ prefix, name = prefixed_name.to_s.split(':')
157
+
158
+ if name
159
+ # Ensure we always use the same prefix for a given namespace
160
+ prefix = prefixes.fetch(namespaces.fetch(prefix))
161
+ else
162
+ name = prefix
163
+ prefix = prefixes.fetch(default_namespace)
164
+ end
165
+
166
+ [prefix, name]
167
+ end
120
168
  end
121
169
  end
data/lolsoap.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "lolsoap"
8
- s.version = "0.1.4"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jon Leighton"]
12
- s.date = "2012-05-17"
12
+ s.date = "2013-05-15"
13
13
  s.description = "A library for dealing with SOAP requests and responses. We tear our hair out so you don't have to."
14
14
  s.email = "j@jonathanleighton.com"
15
15
  s.extra_rdoc_files = [
@@ -45,6 +45,7 @@ Gem::Specification.new do |s|
45
45
  "lolsoap.gemspec",
46
46
  "test/fixtures/stock_quote.wsdl",
47
47
  "test/fixtures/stock_quote_fault.xml",
48
+ "test/fixtures/stock_quote_fault_soap_1_1.xml",
48
49
  "test/fixtures/stock_quote_response.xml",
49
50
  "test/helper.rb",
50
51
  "test/integration/test_client.rb",
@@ -67,7 +68,7 @@ Gem::Specification.new do |s|
67
68
  s.homepage = "http://github.com/loco2/lolsoap"
68
69
  s.licenses = ["MIT"]
69
70
  s.require_paths = ["lib"]
70
- s.rubygems_version = "1.8.15"
71
+ s.rubygems_version = "1.8.24"
71
72
  s.summary = "A library for dealing with SOAP requests and responses."
72
73
 
73
74
  if s.respond_to? :specification_version then
@@ -6,22 +6,25 @@
6
6
  xmlns:xsd1="http://example.com/stockquote.xsd"
7
7
  xmlns:xsd2="http://example.com/stockquote2.xsd"
8
8
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap12/"
9
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
9
10
  xmlns="http://schemas.xmlsoap.org/wsdl/">
10
11
 
11
12
  <types>
12
13
  <schema targetNamespace="http://example.com/stockquote.xsd"
14
+ xmlns:xsd3="http://example.com/stockquote.xsd"
13
15
  xmlns="http://www.w3.org/2001/XMLSchema">
14
16
  <element name="TradePrice">
15
17
  <complexType>
16
18
  <all>
17
- <element name="price" type="float"/>
19
+ <element name="xsd3:price" type="xs:float"/>
18
20
  </all>
19
21
  </complexType>
20
22
  </element>
21
- <complexType name="TradePriceRequest">
23
+ <complexType name="xsd3:TradePriceRequest">
22
24
  <sequence>
23
- <element name="tickerSymbol" type="string" maxOccurs="5"/>
25
+ <element name="tickerSymbol" type="xs:string" maxOccurs="5"/>
24
26
  <element name="specialTickerSymbol" type="xsd2:TickerSymbol" maxOccurs="unbounded"/>
27
+ <attribute name="id" type="xs:string"/>
25
28
  </sequence>
26
29
  </complexType>
27
30
  </schema>
@@ -30,7 +33,7 @@
30
33
  xmlns="http://www.w3.org/2001/XMLSchema">
31
34
  <complexType name="TickerSymbol">
32
35
  <sequence>
33
- <element name="name" type="string" maxOccurs="1"/>
36
+ <element name="name" type="xs:string" maxOccurs="1"/>
34
37
  </sequence>
35
38
  </complexType>
36
39
  </schema>
@@ -41,7 +44,7 @@
41
44
  </message>
42
45
 
43
46
  <message name="GetLastTradePriceOutput">
44
- <part name="body" element="xsd1:TradePrice"/>
47
+ <part name="body" element="xsd3:TradePrice"/>
45
48
  </message>
46
49
 
47
50
  <portType name="StockQuotePortType">