I have a problem with a WCF web services that is deserialising an array of polymorphic objects. The server side is not WCF, but I created the stubs from WSDL. The important parts in WSDL are
<complexType name="NodeList">
<sequence>
<element name="data" type="cmtypes:Node" minOccurs="0" maxOccurs="unbounded" nillable="true"/>
</sequence>
</complexType>
and
<complexType name="Device">
<sequence>
<element name="uniqueKey" type="xsd:unsignedLong" minOccurs="1" maxOccurs="1"/>
<element name="revision" type="xsd:string" minOccurs="1" maxOccurs="1"/>
[...}
</sequence>
</complexType>
<complexType name="Node">
<complexContent>
<extension base="cmtypes:Device">
<sequence>
<element name="cmdaemonUrl" type="xsd:string" minOccurs="1" maxOccurs="1"/>
<element name="networks" type="cmtypes:NetworkInterfaceList" minOccurs="0" maxOccurs="1" nillable="true"/>
[...]
</sequence>
</extension>
</complexContent>
</complexType>
<complexType name="MasterNode">
<complexContent>
<extension base="cmtypes:Node">
<sequence>
</sequence>
</extension>
</complexContent>
</complexType>
VS created the following type for the list:
[System.Xml.Serialization.SoapIncludeAttribute(typeof(MasterNode))]
[System.Xml.Serialization.SoapIncludeAttribute(typeof(SlaveNode))]
[System.Xml.Serialization.SoapIncludeAttribute(typeof(VirtualSMPNode))]
[System.Xml.Serialization.SoapIncludeAttribute(typeof(VirtualNode))]
[System.Xml.Serialization.SoapIncludeAttribute(typeof(PhysicalNode))]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.233")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.SoapTypeAttribute(Namespace="http://www.brightcomputing.com/cmtypes.xsd")]
public partial class Node : Device {
private string cmdaemonUrlField;
[...]
}
However, if I receive a message containing a master node like
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cmtypes="http://www.brightcomputing.com/cmtypes.xsd" xmlns:cmdevice="http://www.brightcomputing.com/cmdevice.wsdl">
<s:Header xmlns:s="http://www.w3.org/2003/05/soap-envelope"></s:Header>
<SOAP-ENV:Body>
<cmdevice:getNodesResponse SOAP-ENV:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<nodes xsi:type="cmtypes:NodeList" xmlns="">
<data xsi:type="cmtypes:MasterNode">
<uniqueKey xsi:type="xsd:unsignedLong">38654705666</uniqueKey>
<revision xsi:type="xsd:string"></revision>
<modified xsi:type="xsd:boolean">false</modified>
<toBeRemoved xsi:type="xsd:boolean">false</toBeRemoved>
[...]
the whole stuff explodes with a System.ServiceModel.CommunicationException saying "Error in deserializing body of reply message for operation 'getNodes'." and "Object cannot be stored in an array of this type." in the inner exception. Why? What can I do to fix this (the stub generation)?
I have absolutely no clue due to very limited WCF/SOAP knowledge, so any help is appreciated.
Best regards,
Christoph
Edit: It might be relevant that the response actually contains "data" elements of different, polymorphic types, i.e. cmtypes:MasterNode and cmtypes:PhysicalNode.
Edit: Could it be that the Stub and the Message do not go together? As I understand, the stub expects a data property, which is an array:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.233")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.SoapTypeAttribute(Namespace="http://www.brightcomputing.com/cmtypes.xsd")]
public partial class NodeList : object, System.ComponentModel.INotifyPropertyChanged {
private Node[] dataField;
/// <remarks/>
[System.Xml.Serialization.SoapElementAttribute(IsNullable=true)]
public Node[] data {
get {
return this.dataField;
}
set {
this.dataField = value;
this.RaisePropertyChanged("data");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null)) {
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
However, in my answer I get a NodeList directly containing a sequence of data elements, which are actually nodes or derived from nodes:
<cmdevice:getNodesResponse SOAP-ENV:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<nodes xsi:type="cmtypes:NodeList" xmlns="">
<data xsi:type="cmtypes:MasterNode">
<uniqueKey xsi:type="xsd:unsignedLong">38654705666</uniqueKey>
[...]
</data>
<data xsi:type="cmtypes:PhysicalNode">
<uniqueKey xsi:type="xsd:unsignedLong">38654705669</uniqueKey>
[...]
</data>
[...]
Do you think I can fix that somehow by injecting a behavior in WCF? My big problem is that I have absolutely no possibility of chaning the server side...
Troubleshooting this is going to be hard. One thing I would try is using XmlSerializer to see if it can deserialize then start reducing the message until you get the offending type.
Related
I am using eclipse link(v2.5.1) Dynamic JAXB to convert XML to JSON and viceversa using a multiple schemas. While umarshalling the JSON to XML,I want to validate the JSON(against the XSD) and Dynamic Moxy is unable to validate the generated JSON and results in classcast exception.
emp.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:emp="Employee:2:0" targetNamespace="Employee:2:0"
elementFormDefault="unqualified" attributeFormDefault="unqualified"
version="2.0">
<xsd:element name="searchManager" type="emp:SearchManager" />
<xsd:complexType name="SearchManager">
<xsd:sequence>
<xsd:element name="CompanyName" type="xsd:string" />
<xsd:element name="objects" type="emp:Employee" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Employee">
<xsd:complexContent>
<xsd:extension base="emp:Organization">
<xsd:sequence>
<xsd:element name="EmpId" type="xsd:string" minOccurs="0" />
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Projects">
<xsd:complexContent>
<xsd:extension base="emp:Organization"/>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Organization">
<xsd:annotation>
<xsd:documentation>Abstract base class </xsd:documentation>
</xsd:annotation>
</xsd:complexType>
</xsd:schema>
Manager.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="Manager:1:0" xmlns:emp="Employee:2:0"
xmlns:manager="Manager:1:0" xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="unqualified" attributeFormDefault="unqualified"
version="1.0">
<!-- schema imports -->
<xs:import namespace="Employee:2:0" schemaLocation="emp.xsd" />
<xs:complexType name="Manager">
<xs:annotation>
<xs:documentation>
Definition of class Employee
</xs:documentation>
</xs:annotation>
<xs:complexContent>
<xs:extension base="emp:Employee">
<xs:sequence>
<xs:element name="teamSize" type="xsd:int" minOccurs="0" />
<xs:element name="project1" type="manager:Project1"
minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="Project1">
<xs:complexContent>
<xs:extension base="manager:Developement">
<xs:sequence>
<xs:element name="type" type="xsd:int" minOccurs="0" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="Developement">
<xs:annotation>
<xs:documentation>
Abstract base class for an Development
</xs:documentation>
</xs:annotation>
<xs:complexContent>
<xs:extension base="emp:Projects"/>
</xs:complexContent>
</xs:complexType>
</xs:schema>
sample.xml
<emp:searchManager xmlns:emp="Employee:2:0"
xmlns:manager="Manager:1:0">
<CompanyName>Test</CompanyName>
<objects xmlns:ns2="Manager:1:0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns2:Manager">
<EmpId>123456</EmpId>
<teamSize>10</teamSize>
<project1>
<type>1</type>
</project1>
</objects>
</emp:searchManager>
The driver program to test is as follows
public class XMLToJSON {
/**
* #param args
*/
public static void main(String[] args) {
FileInputStream xsdInputStream;
try {
xsdInputStream = new FileInputStream("Manager.xsd");
DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory.createContextFromXSD(xsdInputStream, new MyEntityResolver(), null, null);
FileInputStream xmlInputStream = new FileInputStream("sample.xml");
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
JAXBElement<DynamicEntity> manager = (JAXBElement) unmarshaller.unmarshal(xmlInputStream);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
//input xml to print
marshaller.marshal(manager, System.out);
Map namespaces = new HashMap();
namespaces.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
namespaces.put("Employee:2:0", "ns1");
namespaces.put("Manager:1:0", "ns2");
// XML to JSON
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
marshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, namespaces);
FileOutputStream jsonOutputStream = new FileOutputStream("sample.json");
marshaller.marshal(manager, jsonOutputStream);
marshaller.marshal(manager, System.out);
//JSON to XML
JAXBUnmarshaller jsonUnmarshaller = jaxbContext.createUnmarshaller();
jsonUnmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");
jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_NAMESPACE_PREFIX_MAPPER, namespaces);
jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, true);
jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_ATTRIBUTE_PREFIX, "#");
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(new File("Manager.xsd"));
jsonUnmarshaller.setSchema(schema);
StreamSource json = new StreamSource("sample.json");
JAXBElement<DynamicEntity> myroot = (JAXBElement) jsonUnmarshaller.unmarshal(json);
Marshaller m = jaxbContext.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
m.marshal(myroot, System.out);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (JAXBException e) {
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Generated Sample JSON
{
"ns1.searchManager" : {
"CompanyName" : "Test",
"objects" : [ {
"xsi.type" : "ns2.Manager",
"EmpId" : "123456",
"teamSize" : 10,
"project1" : [ {
"type" : 1
} ]
} ]
}
}
While unmarshalling the below exception was seen
Exception in thread "main" java.lang.ClassCastException: org.eclipse.persistence.internal.oxm.record.XMLReaderAdapter$ExtendedContentHandlerAdapter cannot be cast to org.eclipse.persistence.internal.oxm.record.UnmarshalRecord
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.isTextValue(JSONReader.java:454)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:350)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:244)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:430)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:299)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parseRoot(JSONReader.java:166)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:125)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:140)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:778)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:666)
at org.eclipse.persistence.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:593)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:287)
at XMLToJSON.main(XMLToJSON.java:74)
EDIT:Corrected the generated sample json
Question
The intention is to check whether JSON conforms to the XML schema. Does Dynamic JAXB support JSON Validation while unmarshalling? Is this a bug in Dynamic JAXB Moxy?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You are hitting the following issue which was fixed in the 2.5.1 (and 2.6.0) streams.
http://bugs.eclipse.org/407498
The fix is available in EclipseLink 2.5.1 (and 2.6.0) in the nightly builds starting September 11, 2013 from the following link:
http://www.eclipse.org/eclipselink/downloads/nightly.php
Schema Validation and JSON Unmarshalling
Due to how we need to process the JSON document, validating the JSON input against an XML schema is not guaranteed to work. Below is the exception you will get for your use case once you get the ClassCastException fix.
javax.xml.bind.UnmarshalException
- with linked exception:
[Exception [EclipseLink-25004] (Eclipse Persistence Services - #VERSION#.#QUALIFIER#): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document
Internal Exception: org.xml.sax.SAXParseException; cvc-complex-type.2.4.d: Invalid content was found starting with element 'teamSize'. No child element is expected at this point.]
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.handleXMLMarshalException(JAXBUnmarshaller.java:980)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:290)
at forum18824990.XMLToJSON.main(XMLToJSON.java:63)
Caused by: Exception [EclipseLink-25004] (Eclipse Persistence Services - #VERSION#.#QUALIFIER#): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document
Internal Exception: org.xml.sax.SAXParseException; cvc-complex-type.2.4.d: Invalid content was found starting with element 'teamSize'. No child element is expected at this point.
at org.eclipse.persistence.exceptions.XMLMarshalException.unmarshalException(XMLMarshalException.java:115)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:144)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:784)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:666)
at org.eclipse.persistence.internal.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:566)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:287)
... 1 more
Caused by: org.xml.sax.SAXParseException; cvc-complex-type.2.4.d: Invalid content was found starting with element 'teamSize'. No child element is expected at this point.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:198)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:437)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:368)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:325)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator$XSIErrorReporter.reportError(XMLSchemaValidator.java:453)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.reportSchemaError(XMLSchemaValidator.java:3232)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.handleStartElement(XMLSchemaValidator.java:1795)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.startElement(XMLSchemaValidator.java:741)
at com.sun.org.apache.xerces.internal.jaxp.validation.ValidatorHandlerImpl.startElement(ValidatorHandlerImpl.java:565)
at org.eclipse.persistence.internal.oxm.record.XMLReader$ValidatingContentHandler.startElement(XMLReader.java:431)
at org.eclipse.persistence.internal.oxm.record.XMLReaderAdapter$ExtendedContentHandlerAdapter.startElement(XMLReaderAdapter.java:178)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:302)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:436)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:418)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:244)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:436)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:303)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parseRoot(JSONReader.java:166)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:125)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:140)
... 5 more
I will describe my problem with xml.
first i defined my xsd schema with namespaces and it looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/schema"
xmlns:tns="http://www.example.org/schema" elementFormDefault="qualified">
<element name="Clients" type="tns:ClientsType"></element>
<complexType name="ContactType">
<sequence>
<element name="Type" type="string"></element>
</sequence>
<attribute name="ID" type="long" />
</complexType>
<complexType name="Contact">
<sequence>
<element name="ContactType" type="tns:ContactType"></element>
<element name="Value" type="string"></element>
</sequence>
<attribute name="ID" type="long" />
</complexType>
<complexType name="ContactsType">
<sequence>
<element name="Contact" type="tns:Contact" maxOccurs="unbounded"></element>
</sequence>
</complexType>
<complexType name="ClientType">
<sequence>
<element name="FirstName" type="string"></element>
<element name="SecondName" type="string"></element>
<element name="Contacts" type="tns:ContactsType"></element>
</sequence>
<attribute name="ID" type="long" />
</complexType>
<complexType name="ClientsType">
<sequence>
<element name="Client" type="tns:ClientType" maxOccurs="unbounded"></element>
</sequence>
</complexType>
so it generated obvious xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<tns:Clients xmlns:tns="http://www.example.org/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/schema schema.xsd ">
<tns:Client ID="0">
<tns:FirstName>tns:FirstName</tns:FirstName>
<tns:SecondName>tns:SecondName</tns:SecondName>
<tns:Contacts>
<tns:Contact ID="0">
<tns:ContactType ID="0">
<tns:Type>tns:Type</tns:Type>
</tns:ContactType>
<tns:Value>tns:Value</tns:Value>
</tns:Contact>
</tns:Contacts>
</tns:Client>
and my function creating specific xml with data:
#Override
public void printToFile() throws IOException, XMLStreamException {
FileWriter fd = new FileWriter(outPath);
Random rand = new Random();
int end = rand.nextInt(1000);
try{
XMLOutputFactory xof = XMLOutputFactory.newInstance();
XMLStreamWriter xtw = null;
xtw = xof.createXMLStreamWriter(fd);
xtw.writeStartDocument();
//xtw.setPrefix("tns", "http://www.example.org/schema");
xtw.writeStartElement("Clients");
xtw.writeNamespace("tns", "http://www.example.org/schema");
xtw.writeStartElement("tns","Client", "http://www.example.org/schema");
//xtw.writeStartElement("Client");
xtw.writeAttribute("ID", "1");
xtw.writeStartElement("FirstName");
xtw.writeCharacters("NameFirst");
xtw.writeEndElement();
xtw.writeStartElement("SecondName");
xtw.writeCharacters("NameSecond");
xtw.writeEndElement();
xtw.writeStartElement("Contacts");
for(int i=1;i<2;i++){
xtw.writeStartElement("Contact");
xtw.writeAttribute("ID",Integer.toString(i));
xtw.writeStartElement("ContactType");
xtw.writeAttribute("ID", Integer.toString(i));
xtw.writeStartElement("Type");
xtw.writeCharacters("mojTyp");
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeStartElement("Value");
xtw.writeCharacters("value");
xtw.writeEndElement();
xtw.writeEndElement();
}
xtw.writeEndElement();
//xtw.writeEndElement();
xtw.writeEndDocument();
}catch(XMLStreamException xmlE)
{
}
finally
{
fd.close();
}
}
and I generate simple Junit test usinx Sax to validate my xml created by my function:
#Test
public void testPrintToFileGoodXml() throws IOException,
XMLStreamException, FactoryConfigurationError, SAXException {
mService.printToFile();
XMLStreamReader reader = XMLInputFactory.newInstance()
.createXMLStreamReader(new FileInputStream(myPath));
SchemaFactory factory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new File(mySchemaPath));
Validator validator = schema.newValidator();
validator.validate(new StAXSource(reader));
assertTrue(true);
}
But the test is fail so I think there is problem with my printToFile function connected with namespaces and the other properties defined in the xsd schema, and I don't know how to solve it. I read many sites and the documentation of defining xsd and I still don't know how to solve my problem. Please help.
The schema says the root element should be Clients
However your XML Snippet has an opening element tns:Clients, but no coresponsing closing element. Check your writeEndElement calls tally up.
<?xml version="1.0" encoding="UTF-8"?>
<tns:Clients xmlns:tns="http://www.example.org/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/schema Contacts.xsd ">
<tns:Client ID="0">
<tns:FirstName>tns:FirstName</tns:FirstName>
<tns:SecondName>tns:SecondName</tns:SecondName>
<tns:Contacts>
<tns:Contact ID="0">
<tns:ContactType ID="0">
<tns:Type>tns:Type</tns:Type>
</tns:ContactType>
<tns:Value>tns:Value</tns:Value>
</tns:Contact>
</tns:Contacts>
</tns:Client>
</tns:Clients> <--------- Missing
I need to create a DataSet.xsd file according to a NHibernate.hbm.xml file or a Class File. These are Mapping files and class files than we use to work with our DB.
You may ask me, why I do need a DataSet file, generating from a Nhibernate.hbm.xml file or a .cs file?
It's because we're using Crystal Reports, and we're using Frameworks, MVVM, INotifyPropertyChanged, NHibernate and after a lot of studying, for our case, would be better if we Convert an object while in executing time of program and use it to generate a report. If this tool exists, it will make easier, 'cause we have over 60, 70 columns per table in our DB. Since we're already with all these components working into our Project, it wouldn't make sense if we start to use sql queries to generate reports.
This tool, or 'the way to' create this DataSet file, need to read those file in these formats:
Nhibernate.hbm.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="PCT.Domain" namespace="Gerdau.PCT.Kernel.Domain">
<class name="Furnace" table="Furnace" lazy="false">
<id name="Id" column="Id_Furnace" type="Int64">
<generator class="Geraes.GLib.GDomainBasis.CustomTableHiLoGenerator, GLib.GDomainBasis" />
</id>
<property name="Name" column="Name" type="String" length="50" not-null="true"/>
<property name="Code" column="Code" type="Char" not-null="true"/>
<property name="Mill" column="Mill" type="String" length="2" not-null="true"/>
<property name="DischEnabled" column="Disch_Enabled" type="Char" not-null="true"/>
<property name="DischEnabledTemp" column="Disch_Enabled_Temp" type="Char" not-null="true"/>
<property name="ChargeEnabled" column="Charge_Enabled" type="Char" not-null="true"/>
<property name="ChargeEnabledTemp" column="Charge_Enabled_Temp" type="Char" not-null="true"/>
</class>
</hibernate-mapping>
CSharp .cs File:
public class Furnace : BaseEntity
{
public virtual String Name { get; set; }
public virtual Char Code { get; set; }
public virtual String Mill { get; set; }
public virtual Char DischEnabled { get; set; }
public virtual Boolean DischEnabledConv
{
get
{
return DischEnabled.ConvertYesNoToBoolean();
}
set
{
DischEnabled = ((Boolean)value).ConvertYesNoToBoolean();
}
}
public virtual Boolean DischEnabledTempConv
{
get
{
return DischEnabledTemp.ConvertYesNoToBoolean();
}
set
{
DischEnabledTemp = ((Boolean)value).ConvertYesNoToBoolean();
}
}
public virtual Char DischEnabledTemp { get; set; }
public virtual Char ChargeEnabled { get; set; }
public virtual Boolean ChargeEnabledConv
{
get
{
return ChargeEnabled.ConvertYesNoToBoolean();
}
set
{
ChargeEnabled = ((Boolean)value).ConvertYesNoToBoolean();
}
}
public virtual Boolean ChargeEnabledTempConv
{
get
{
return ChargeEnabledTemp.ConvertYesNoToBoolean();
}
set
{
ChargeEnabledTemp = ((Boolean)value).ConvertYesNoToBoolean();
}
}
public virtual Char ChargeEnabledTemp { get; set; }
public virtual ConfiguracaoForno MillConv
{
get
{
if (Mill == ConfiguracaoForno.SM.ToString())
return ConfiguracaoForno.SM;
if (Mill == ConfiguracaoForno.PM.ToString())
return ConfiguracaoForno.PM;
else
return ConfiguracaoForno.NN;
}
}
public override String ToString()
{
return "Forno " + Code + " (" + Name + "): Laminador " + MillConv;
}
}
I know it's an very specific case, but if you can show to us at least a way, it'll be of great help.
Best regards,
Gustavo
Edit:
Found a way to do it: using XSD.EXE, a internal Tool from Visual Studio, i've extracted the mapping from my assembly:
C:\>xsd / c /l:CS -t:Furnace <MyAssembly>.dll -o:"D:\Temp"
But now, when I do this:
D:\>xsd /c schema0.xsd
Results in these errors:
D:\Temp>xsd /c schema0.xsd
Microsoft (R) Xml Schemas/DataTypes support utility
[Microsoft (R) .NET Framework, Version 4.0.30319.1]
Copyright (C) Microsoft Corporation. All rights reserved.
Schema validation warning: Type 'char' is not declared, or is not a simple type.
Line 21, position 5.
Schema validation warning: Type 'http://microsoft.com/wsdl/types/:char' is not declared. Line 10, position 7.
Schema validation warning: Type 'http://microsoft.com/wsdl/types/:char' is not declared. Line 12, position 7.
Schema validation warning: Type 'http://microsoft.com/wsdl/types/:char' is not declared. Line 15, position 7.
Schema validation warning: Type 'http://microsoft.com/wsdl/types/:char' is not declared. Line 16, position 7.
Schema validation warning: Type 'http://microsoft.com/wsdl/types/:char' is not declared. Line 19, position 7.
Warning: Schema could not be validated. Class generation may fail or may produce incorrect results.
Error: Error generating classes for schema 'schema0'.
- The datatype 'char' is missing.
If you would like more help, please type "xsd /?".
Here's my generated schema.xsd:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="http://microsoft.com/wsdl/types/" />
<xs:element name="Furnace" nillable="true" type="Furnace" />
<xs:complexType name="Furnace">
<xs:complexContent mixed="false">
<xs:extension base="BaseEntity">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Name" type="xs:string" />
<xs:element minOccurs="1" maxOccurs="1" name="Code" xmlns:q1="http://microsoft.com/wsdl/types/" type="q1:char" />
<xs:element minOccurs="0" maxOccurs="1" name="Mill" type="xs:string" />
<xs:element minOccurs="1" maxOccurs="1" name="DischEnabled" xmlns:q2="http://microsoft.com/wsdl/types/" type="q2:char" />
<xs:element minOccurs="1" maxOccurs="1" name="DischEnabledConv" type="xs:boolean" />
<xs:element minOccurs="1" maxOccurs="1" name="DischEnabledTempConv" type="xs:boolean" />
<xs:element minOccurs="1" maxOccurs="1" name="DischEnabledTemp" xmlns:q3="http://microsoft.com/wsdl/types/" type="q3:char" />
<xs:element minOccurs="1" maxOccurs="1" name="ChargeEnabled" xmlns:q4="http://microsoft.com/wsdl/types/" type="q4:char" />
<xs:element minOccurs="1" maxOccurs="1" name="ChargeEnabledConv" type="xs:boolean" />
<xs:element minOccurs="1" maxOccurs="1" name="ChargeEnabledTempConv" type="xs:boolean" />
<xs:element minOccurs="1" maxOccurs="1" name="ChargeEnabledTemp" xmlns:q5="http://microsoft.com/wsdl/types/" type="q5:char" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="BaseEntity">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="Id" type="xs:long" />
</xs:sequence>
</xs:complexType>
</xs:schema>
I appreciate for any help.
Gustavo
You're missing an XSD file.
UPDATE: Let's assume you're staying with xsd.exe. In this case, modify the import statement as following (I call this file XSD-1.xsd):
<xs:import namespace="http://microsoft.com/wsdl/types/" schemaLocation="XSD-2.xsd" />
Copy the content below in XSD-2.xsd file, in the same folder:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:tns="http://microsoft.com/wsdl/types/" elementFormDefault="qualified" targetNamespace="http://microsoft.com/wsdl/types/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="guid">
<xs:restriction base="xs:string">
<xs:pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="char">
<xs:restriction base="xs:unsignedShort" />
</xs:simpleType>
</xs:schema>
Run the xsd.exe with the following line:
xsd XSD-1.xsd /c
You should get an XSD-1.cs file and some warnings
If you want to use svcutil, you must have the XSD-2.xsd, otherwise it won't work; also, on the plus side you don't have to modify the XSD-1.xsd:
svcutil XSD-1.xsd XSD-2.xsd /dconly
It works fine, without any errors. I am running v4.
Adding new members in the Data contract are harmless for all versions of clients. Its a known fact.
I wanted to test this.
I created a Book Data Contract. Its first version had the following members.
public class Book
{
[DataMember(IsRequired = false)]
public long BookID;
[DataMember(IsRequired = true)]
public string Title;
[DataMember(IsRequired = false)]
public string Author;
}
The client was created for this version of Contract.
Then I added another member "Edition" to the Book and the new version of the Book is
public class Book
{
[DataMember(IsRequired = false)]
public long BookID;
[DataMember(IsRequired = true)]
public string Title;
[DataMember(IsRequired = false)]
public string Author;
[DataMember(IsRequired = false)]
public string Edition;
}
The newer version of the contract works flawlessly with the older vesion of client.
I wanted to Physically see the on wire soap message. I have enabled the client and server side message logging.
I understand that new member "Edition" would be taken as Nil and would be while serializing.
But on the client side message, I saw something strange, the following.
The soap envelop looks like this
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://www.8fingergenie.com/BookShop/CheckAvailability</Action>
<ActivityId CorrelationId="631f540a-915b-4203-8fd8-e251da2fab85" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">68a15797-124d-47f7-a85a-cf5c661e8011</ActivityId>
</s:Header>
<s:Body>
<CheckAvailability xmlns="http://www.8fingergenie.com/">
<book xmlns:d4p1="http://8fingergenie.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:Author>Spensor Johnson</d4p1:Author>
<d4p1:BookID>1</d4p1:BookID>
<d4p1:Edition i:nil="true"></d4p1:Edition>
<d4p1:Title>Who Moved My Cheese</d4p1:Title>
</book>
</CheckAvailability>
</s:Body>
My question is how did the client with older version of Data contract know about edition while making communication? or am I missing something here.
Following is the structure of the client side Book's schema.
<xs:schema xmlns:tns="http://8fingergenie.com" elementFormDefault="qualified" targetNamespace="http://8fingergenie.com" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="Book">
<xs:sequence>
<xs:element minOccurs="0" name="Author" nillable="true" type="xs:string" />
<xs:element minOccurs="0" name="BookID" type="xs:long" />
<xs:element name="Title" nillable="true" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="Book" nillable="true" type="tns:Book" />
It is not harmless for all clients. You could write a client that would fail if any members were added, deleted, or changed.
The fact that a client could be deliberately written to fail in that situation implies that it's possible to write a client which would inadvertently fail in the same situation.
On Further research I have found the following.
I was using Service reference in the client. The service reference by default creates the Data contract as type derived from IExtensibleDataObject. I didn't notice this would be the default.
So since the type is derived from IExtensibleDataObject, the new members from the other end is wraped inside the ExtensionDataObject. This is how the client was aware of the datacontract's new member.
Thank you for everyone who tried to help me.
We've observed that when we expose a WCF service which uses classes decorated with various xml serialisation attributes, despite the fact that we use the XmlSerializerFormat attribute on the interface any XmlRoot attribute on any of the operation's parameters gets completely ignored.
The namespace of the parameters is always that of the service and not what we specify.
This is causing us problems as it does not seem to be backwards compatible with ASMX and also because we're using BizTalk, and need to have tighter control over the shape of the XML's exchanged.
A few questions then -
Anybody knows what is the rationale
behind this decision?
Anybody knows
how this is happening? I was under
the impressions that WCF, with the
XmlSerializerFormat attribute, uses
the XmlSerialiser to serialise the
types, which would suggest XmlRoot
should be taken into account, how
come this is not the case? (is it
only due to the fact that, taking
the SOAP envelope into account, the
parameter is not root?)
Most
importantly - anybody knows if
there's a way to 'force the issue' -
i.e. get the parameters to be of the
namespace of our choosing?
I've seen this post, but I don't believe it is relevant to my question -
As per Wagner Silveira's request - the contracts I used to test this are -
[ServiceContract(Namespace = "http://servicecontract"),
XmlSerializerFormat(Style = OperationFormatStyle.Document)]
public interface ITestService
{
[OperationContract]
MyOtherType MyTestMethod(MyType obj);
}
// Composite class for DCS and XMLS
[Serializable, XmlType, XmlRoot(Namespace = "http://datacontract")]
public class MyType
{
[XmlAttribute]
public string StringValue { get; set; }
}
// Composite class for DCS and XMLS
[Serializable, XmlType, XmlRoot(Namespace = "http://datacontract")]
public class MyOtherType
{
[XmlAttribute]
public string OtherStringValue { get; set; }
}
I assume you're using SOAP as the message format. In this case, the object you're serializing is not the root of the XML, the soap envelope is. So it makes sense that the XmlRoot would be ignored. By default WCF will create a message contract for you and name the response and it has the namespace of the service. What you can do is create your own message contract to have full control over SOAP.
Create the following two classes:
[MessageContract]
public class MyTestMethodRequest
{
[MessageBodyMember( Namespace = "http://datacontract" )]
public MyType MyType;
}
[MessageContract]
public class MyTestMethodResponse
{
[MessageBodyMember( Namespace = "http://datacontract" )]
public MyOtherType MyOtherType;
}
Then change the signature of your service operation to the following.
[OperationContract]
public MyTestMethodResponse MyTestMethod( MyTestMethodRequest request )
{
return new MyTestMethodResponse {
MyOtherType = new MyOtherType {
OtherStringValue = "bar"
}
};
}
Now if you example the SOAP messages you should see the following:
Request
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"
s:mustUnderstand="1">http://servicecontract/TestService/MyTestMethod</Action>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyTestMethodRequest xmlns="http://servicecontract">
<MyType StringValue="foo" xmlns="http://datacontract" />
</MyTestMethodRequest>
</s:Body>
</s:Envelope>
Response
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header />
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyTestMethodResponse xmlns="http://servicecontract">
<MyOtherType OtherStringValue="bar" xmlns="http://datacontract" />
</MyTestMethodResponse>
</s:Body>
</s:Envelope>
I don't know why WCF ignores XmlRoot, so I can't answer that part of your question. But I do have a couple ways to solve the problem.
start with WSDL first.
If you have a particular set of XML namespaces you would like to apply to the messages that get sent and receieved, use WSDL and XML Schema to explicitly specify them.
Then, generate the Server-side stub code, or the client-side proxy code, directly from that WSDL via the svcutil.exe tool.
use a custom ServiceHost
The other option open to you, described at this link, is to use a custom ServiceHost that overrides WCF's decision to disregard the XmlRoot or XmlType attributes on message types.
If you choose to go for the WSDL-First approach, the WSDL should look like this:
<?xml version="1.0" encoding="utf-8" ?>
<definitions
xmlns="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="urn:The-Service-namespace"
xmlns:tns="urn:The-Service-namespace"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:n0="urn:The-Request-namespace"
xmlns:n1="urn:The-Response-namespace"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
elementFormDefault= "unqualified"
>
<types>
<s:schema targetNamespace="urn:The-Request-namespace" >
<s:complexType name="Type1">
<s:sequence>
<s:element name="x" minOccurs="1" maxOccurs="1" type="s:string"/>
</s:sequence>
</s:complexType>
<s:element name="Type1" type="n0:Type1" />
</s:schema>
<s:schema targetNamespace="urn:The-Response-namespace" >
<s:complexType name="Type2">
<s:sequence>
<s:element name="x" minOccurs="1" maxOccurs="1" nillable="false" type="s:string"/>
<s:element name="y" minOccurs="1" maxOccurs="1" nillable="false" type="s:int"/>
<s:element name="z" minOccurs="1" maxOccurs="1" nillable="false" type="s:boolean" />
</s:sequence>
</s:complexType>
<s:element name="Type2" type="n1:Type2" />
</s:schema>
</types>
<message name="RequestMessage">
<part name="inPart1" element="n0:Type1" />
</message>
<message name="ResponseMessage">
<part name="outPart1" element="n1:Type2" />
</message>
<portType name="PortTypeName">
<operation name="Method1">
<input message="tns:RequestMessage" />
<output message="tns:ResponseMessage" />
</operation>
</portType>
<binding name="InterfaceName" type="tns:PortTypeName">
<soap:binding
transport="http://schemas.xmlsoap.org/soap/http"
style="rpc" />
<operation name="Method1">
<soap:operation soapAction="" style="document" />
<input> <soap:body use="literal" /> </input>
<output> <soap:body use="literal" /> </output>
</operation>
</binding>
</definitions>
This WSDL is very simple - it defines a single operation, with a single request message and a single response message.
Notice there are three xml namespaces:
urn:The-Service-namespace
used for the element that wraps the request and response - the first element inside the <SOAP:body>
urn:The-Request-namespace
used for the element wrapped inside that request wrapper, which gets deserialized into an instance of Type1.
urn:The-Response-namespace
used for the element wrapped inside that response wrapper, which gets deserialized into an instance of Type2.
If your web services interface is more complicated, has more operations and consequently more request and response message types, you can add more namespaces, if you like, for all those additional types.