I am using savon 0.9.2 and ruby 1.8.7.
I am trying to make a complex type soap request.
I need to figure out how to code the soap body for the below type of request using ruby and savon. Basically one of the complextypes in the request extends another type and also needs to be encoded as an array.
The soap request object is supposed to look like this.
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:app="http://someurl/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<soapenv:Header/>
<soapenv:Body>
<app:someMethod soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<xyzResReq xsi:type="java:xyzResReq" xmlns:java="java:com.xyz.request">
<somestring xsi:type="xsd:string">abc123</somestring>
<itinerary xsi:type="java1:xyzItinerary" xmlns:java1="java:com.xyz.domain">
<someList xsi:type="java2:List" soapenc:arrayType="xsd:anyType[]" xmlns:java2="java:language_builtins.util"/>
</itinerary>
</xyzResReq>
</app:someMethod>
</soapenv:Body>
</soapenv:Envelope>
someList is again a complextype in the schema form
<xsd:complexType name="someList">
<xsd:complexContent>
<xsd:extension base="stns:someBaseList">
<xsd:sequence>
<xsd:element maxOccurs="1" name="someElement" type="xsd:boolean" minOccurs="0" />
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
and someBaseList defined as
<xsd:complexType name="someBaseList">
<xsd:sequence>
<xsd:element maxOccurs="1" nillable="true" name="baseElement" type="xsd:string" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
How do I do this in savon.
Savon is based on the assumption that most requests (XML) are simple enough to be abstracted as a Hash. In this complex example, I'd suggest two alternatives:
Instead of a Hash, you can use any Ruby object (that's not a Hash) and responds to to_s. So you could create an object (or a hierarchy of objects) with a to_s method constructing the XML via something like Builder and pass it to Savon::SOAP::XML#body=.
class SomeXML
def self.to_s
"<some>xml</some>"
end
end
client.request :some_action do
soap.body = SomeXML
end
You could also use Savon::SOAP::XML#xml, which yields a Builder instance to a given block to construct the XML "on the fly".
client.request :some_action do
soap.xml do |xml|
xml.person { |b| b.name("Jim"); b.phone("555-1234") }
end
end
Hope that helps! Also, please take a look at the new Savon Guide.
Related
I cannot find any solution to properly list all public folder mailboxes and public folders using SOAP api. I found only powershell commands or C# methods. If anybody know how to get/list all public folders, please provide me with the solution.
To enumerate Public Folders you need to use the FindFolder operation and then make a Shallow traversal query of each folder level starting at the Root (because you can't do deep traversals) eg
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<soap:Body>
<FindFolder Traversal="Shallow" xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
<FolderShape>
<t:BaseShape>Default</t:BaseShape>
</FolderShape>
<ParentFolderIds>
<t:DistinguishedFolderId Id="publicfoldersroot"/>
</ParentFolderIds>
</FindFolder>
</soap:Body>
</soap:Envelope>
You can't get Public folder Mailboxes using EWS what you should be doing on Office365 is discovering the correct PublicFolder Mailbox to include in the routing headers so you should read through https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-route-public-folder-hierarchy-requests and https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-route-public-folder-content-requests (which both have XML samples for the calls you need).
Firstly, you need to determine the value of the X-AnchorMailbox header using SOAP and make an Autodiscover request to determine the X-PublicFolderInformation value.
Secondly, use FindFolder and then traversal query of each folder level starting at the Root, for example:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2013_SP1" />
</soap:Header>
<soap:Body>
<m:FindFolder Traversal="Shallow">
<m:FolderShape>
<t:BaseShape>AllProperties</t:BaseShape>
</m:FolderShape>
<m:IndexedPageFolderView MaxEntriesReturned="1" Offset="0" BasePoint="Beginning" />
<m:Restriction>
<t:IsEqualTo>
<t:FieldURI FieldURI="folder:DisplayName" />
<t:FieldURIOrConstant>
<t:Constant Value="My Public Contacts" />
</t:FieldURIOrConstant>
</t:IsEqualTo>
</m:Restriction>
<m:ParentFolderIds>
<t:FolderId Id="AQEuAAADy/LIWjRCp0GFb0W6aGPbwwEARg5aCLUc8k6wLfl1c0a/2AAAAwIAAAA=" ChangeKey="AQAAABYAAABGDloItRzyTrAt+XVzRr/YAABdo/XB" />
</m:ParentFolderIds>
</m:FindFolder>
</soap:Body>
</soap:Envelope>
Reference from:
Route public folder hierarchy requests
Route public folder content requests
I want to use the following xpath
/Users/User/UserID
This does not work because the ESB adds a soap envelope and body around my xml, what is the correct xpath to use or how can I remove the soap envelope and body?
The xml is:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<Users xmlns="http://ws.wso2.org/dataservice">
<User>
<UserID>a</UserID>
<Username>a</Username>
<Email>a</Email>
<Password>a</Password>
</User>
<User>
<UserID>a</UserID>
<Username>a</Username>
<Email>a</Email>
<Password>a</Password>
</User>
<User>
<UserID>a</UserID>
<Username>a</Username>
<Email>a</Email>
<Password>a</Password>
</User>
</Users>
</soapenv:Body>
</soapenv:Envelope>
EDIT:
This works when I try to log it outside of my iterate mediator
//*[local-name() = 'Users']/*[local-name() = 'User']/*[local-name() = 'UserID']
but when I try to log it inside the iterate mediator it returns nothing?
Got this working by using the following
<property xmlns:int="http://ws.wso2.org/dataservice" name="uri.var.ID" expression="$body/int:User/int:UserID/text()" scope="default" type="STRING"/>
To access your elements you should go through envelope and body.
Here is an example with switch mediator:
<switch xmlns:ns="http://org.apache.synapse/xsd"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:int="http://ws.wso2.org/dataservice"
source="boolean(/soap:Envelope/soap:Body/int:Users/int:User/int:UserID/text() = 'string_to_compare'">
<case regex="true">
....
</case>
<default/>
</switch>
UPD
corrected XPath expression
Try this:
<property name="yourParamName" expression="$body/Users/User/UserID" scope="default" type="STRING"/>
You can read more on the predefined Synapse XPath variables here.
<DataSet xmlns="http://www.atcomp.cz/webservices">
<xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="file_mame">...</xs:schema>
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<alldata xmlns="">
<category diffgr:id="category1" msdata:rowOrder="0">
<category_code>P.../category_code>
<category_name>...</category_name>
<subcategory diffgr:id="subcategory1" msdata:rowOrder="0">
<category_code>...</category_code>
<subcategory_code>...</subcategory_code>
<subcategory_name>...</subcategory_name>
</subcategory>
....
How can I obtain all categories and subcategories data?
I am trying something like:
reader.xpath('//DataSet/diffgr:diffgram/alldata').each do |node|
But this gives me:
undefined method `xpath' for #<Nokogiri::XML::Reader:0x000001021d1750>
Nokogiri's Reader parser does not support XPath. Try using Nokogiri's in-memory Document parser instead.
On another note, to query xpath namespaces, you need to provide a namespace mapping, like this:
doc = Nokogiri::XML(my_document_string_or_io)
namespaces = {
'default' => 'http://www.atcomp.cz/webservices',
'diffgr' => 'urn:schemas-microsoft-com:xml-diffgram-v1'
}
doc.xpath('//default:DataSet/diffgr:diffgram/alldata', namespaces).each do |node|
# ...
end
Or you can remove the namespaces:
doc.remove_namespaces!
doc.xpath('//DataSet/diffgram/alldata').each { |node| }
I am experiencing a problem with validating an xml file against a schema which was generated by svcutil. For the purpose of this question please see below a snippet of code which contains only a simplified XSD schema and the XML document that I am trying to validate:
Imports System.Xml.Schema
Module Main
Dim errors As Boolean = False
Sub Main()
Try
Dim xsdMarkup As XElement = _
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema' xmlns:tns="http://zen/myservices" targetNamespace="http://zen/myservices">
<xs:element name="Car" type="tns:CarType"/>
<xs:complexType name="CarType">
<xs:sequence>
<xs:element name="Make" minOccurs="1" maxOccurs="1"/>
<xs:element name="Model" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
Dim schemas As XmlSchemaSet = New XmlSchemaSet()
schemas.Add("http://zen/myservices", xsdMarkup.CreateReader)
Dim doc1 As XDocument = _
<?xml version='1.0'?>
<Car>
<Makee>content1</Makee>
<Model>content1</Model>
</Car>
Console.WriteLine("Validating doc1")
errors = False
doc1.Validate(schemas, AddressOf XSDErrors)
Console.WriteLine("doc1 {0}", IIf(errors = True, "did not validate", "validated"))
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.WriteLine("Hit <ENTER> to exit...")
Console.ReadKey()
End Sub
Private Sub XSDErrors(ByVal o As Object, ByVal e As ValidationEventArgs)
Console.WriteLine("{0}", e.Message)
errors = True
End Sub
End Module
The validation in this particular case should fail (the 'Make' element has been misspelled). Interestingly enough though it passes.
Any ideas what am I missing in this code?
Your help is appreciated.
Zen
All set. I resolved the issue myself. I had accidentally left out the namespaces definition in the XML being validated:
<?xml version='1.0'?>
<Car>
<Makee>content1</Makee>
<Model>content1</Model>
</Car>
should have been:
<?xml version='1.0'?>
<tns:Car xmlns:tns="http://zen/myservices">
<Makee>content1</Makee>
<Model>content1</Model>
</tns:Car>
Validation is failing now as expected.
Zen
Does anyone know how to generate an XSD using LinqToXml? I can't find any examples of this anywhere. The XSD will be of the following fairly low level of complexity:
<?xml version="1.0" encoding="utf-8" ?>
<!--Created with Liquid XML Studio 6.1.18.0 - FREE Community Edition (http://www.liquid-technologies.com)-->
<xs:schema
elementFormDefault="qualified"
targetNamespace="http://schemas.xxx.yy/CRM/2009/01/DeadAnimalReport"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Name">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:length value="35" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="Email" type="xs:string" />
<xs:element name="Selection">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:length value="15" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="DeliveryDate" type="xs:date" />
</xs:schema>
The context construction of tooling to allow business analysts to generate message schemas along with some related artefacts that are out of scope of the question. The tooling XSD will be generated from CLR objects in the application's object model.
The objects are pretty simple - a root object that contains enough information to construct the namespace along with a collection of other objects representing the elements (type, name, etc).
Thanks
Sean
Why do you want to use LINQ in this scenario? How does the source data look like?
Not much information given but anyway:
You can construct your XSD using similar code:
XNamespace nsXS = "http://www.w3.org/2001/XMLSchema";
XElement root = new XElement(nsXS + "schema",
new XAttribute("elementFormDefault", "qualified"),
new XAttribute("targetNamespace", "http://schemas.xxx.yy/CRM/2009/01/DeadAnimalReport"),
new XElement(nsXS + "element",
new XElement(nsXS + "simpleType",
new XElement(nsXS + "restriction",
new XAttribute("base", "xs:string")),
new XElement(nsXS + "length", new XAttribute("value", 35)))));
If you have some sort of objects, then you can use projections:
var q =
new XElement(nsXS + "schema",
from s in someObjects
select GetXsdDefinition(s)
);
where
GetXsdDefinition is a method that takes your object as an argument and returns it's XSD definition
Since you want to use LinqToXml, I assume your scenario is that you already have some Xml and you want an Xsd to go with it.
LinqToXml doesn't really have much to do with Xsd's...
You may want to look at Xsd Inference tools.
There is also a LINQ to XSD, maybe thats what you`re looking for!
You can find it HERE