Setting minOccurs=0 in a WSDL using "Specified" pattern not working - c#

OK, obviously I'm doing something wrong here. I'm trying to create a webservice and I want "dateShipped" to be optional which means in the WSDL, I want minOccurs="0"
[Serializable]
[XmlType]
public class CTShipment
{
[XmlElement(Order = 0, IsNullable=false)] public CTDeliveryMethod DeliveryMethod;
[XmlElement(Order = 1, IsNullable=false)] public CTShipmentAddress ShipmentAddress;
[XmlIgnore] public bool dateShippedSpecified;
[XmlElement(Order = 2, IsNullable=false)] public DateTime dateShipped;
}
I want the WSDL to be generated like this:
<xs:complexType name="CTShipment">
<xs:annotation>
<xs:documentation>All details for the shipment of a suborder.</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="DeliveryMethod" type="CTDeliveryMethod" nillable="false"/>
<xs:element name="ShipmentAddress" type="CTShipmentAddress" nillable="false"/>
<xs:element name="dateShipped" type="xs:dateTime" nillable="false" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
Instead what I am actually getting is this:
<xs:complexType name="CTShipment">
<xs:sequence>
<xs:element name="DeliveryMethod" nillable="true" type="tns:CTDeliveryMethod"/>
<xs:element name="ShipmentAddress" nillable="true" type="tns:CTShipmentAddress"/>
<xs:element name="dateShipped" type="xs:dateTime"/>
<xs:element name="dateShippedSpecified" type="xs:boolean"/>
</xs:sequence>
</xs:complexType>
According to several things I've read (including http://msdn.microsoft.com/en-us/library/zds0b35c%28v=vs.90%29.aspx) including the public bool "dateShippedSpecified" should make "dateShipped" optional (minOccurs=0). As you can see, not only is this not happening but "dateShippedSpecified" is showing up in the WSDL even though it is marked with "[XmlIgnore]". You may have noticed that there is another problem as well: even though I'm specifying "IsNullable=false", I still get nillable="true" in the WSDL.
That is no less than 4 problems I can't explain all related to the same thing:
How can I set minOccurs to 0 in my WSDL?
Why isn't the [fieldName]Specified pattern making [fieldName] optional (minOccurs = 0)?
Even if it wasn't following the ___Specified pattern, why would dateShippedSpecified show up in the WSDL if it is marked with XmlIgnore?
Why is everything marked as nillable="true" even though I'm specifying "IsNullable=false"?
and as a bonus question, if anyone knows...
How do I get the annotation (as shown below) to be included?
<xs:annotation>
<xs:documentation>All details for the shipment of a suborder.</xs:documentation>
</xs:annotation>

This is due to Sequence element. It specifies that every element has minOccurs= 1.
And the WSDL uses a Sequence-Element instead of "All" because you specified an Order to them.
This requires that every value is present.
So it should work allready when you remove the Order. If you really need the order, than there is no way for you to leave out that value.

This is a bug in .net implementation.
According to W3C specs (for wsdl) minOccurs="0" can be used within sequence. "< sequence >"
means elements in order occurring 0 or more times.
For example look at official W3C definition of wsdl: http://www.w3.org/TR/wsdl
You will see elements like:
<sequence>
<element ref="wsdl:documentation" minOccurs="0"/>
</sequence>
For now when need to be .Net compatible use nillable="true"
which will give you DateTime? (nullable version) instead of DateTime.

Related

How to add custom Attributes to ComplexType Elements in xsd?

I am generating a c# class file from an XSD provided to me. I am able to do this without problems if using just the original native XSD. However, I would like to make modifications to the XSD by adding my own namespace and I would like to feed the newly modified XSD through the generator to produce a class file that contains class definitions I was provided, as well as definitions I have added.
So my understanding is that I need to add a namespace as well as the new attribute under that namespace. I do have access and the ability to modify the original XSD or add my own xsd.
I looked at this post which was similar Extend XSD with custom attributes?
An example of my attempt is below. In my attempt I am trying to add an attribute "Humidity" to the DeviceInfo element under the namespace of "mns". This currently shows an error of the "'mns:attribute' element is not supported in this context."
test.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://MyNamespace.com"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="DeviceInfo">
<xs:complexType>
<xs:attribute name="Humidity" type="xs:float" />
</xs:complexType>
</xs:element>
</xs:schema>
main.xsd
<xs:schema targetNamespace="http://www.CIP4.org/JDFSchema_2_0"
xmlns="http://www.CIP4.org/JDFSchema_2_0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mns="http://MyNamespace.com"
xsi:schemaLocation="http://MyNamespace.com ./test.xsd"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:element name="DeviceInfo">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" ref="Activity"/>
<xs:element maxOccurs="2" minOccurs="0" ref="FileSpec"/>
<xs:element maxOccurs="unbounded" minOccurs="0" ref="JobPhase"/>
</xs:sequence>
<xs:attribute name="CounterUnit" type="NMTOKEN" use="optional"/>
<mns:Humidity value="0"/>
</xs:schema>
Currently this returns the 'mns:humidity' element is not supported in this context.
Can anybody explain how I can insert this to the complex type?
I also tried placing the mns:Humidity in the same tag as deviceInfo
<xs:element name="DeviceInfo mns:Humidity="0">
Sending that through the class generator produced two separate instances of "DeviceInfo" where the second had appended a 1.
How can I insert it in the complexType with the namespace?
In main.xsd, in the declaration of DeviceInfo, add
<xs:attribute ref="mns:Humidity"/>
plus a namespace declaration for "mns" and an xs:import declaration for that namespace with schemaLocation="test.xsd".
In test.xsd include the single declaration
<xs:attribute name="Humidity" type="xs:float"/>
Your attempt to solve this suggests that you are using trial and error rather than actually reading up what constructs in XSD actually mean. That isn't a good way forward. You seem fairly confused about the concepts, for example a schema should never contain an xsi:schemaLocation attribute.

C# WCF Service Reference exposes concrete class instead of inherited abstract class when abstract class is referenced

I am generating a C# service reference using Visual Studio 2010 from my wsdl.
(Simplified example, please excuse any syntax errors):
<xs:complexType name="Constraints">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="unbounded" ref="p:Constraint" />
</xs:sequence>
</xs:complexType>
<xs:element name="Constraint" type="p:ConstraintType" />
<xs:complexType abstract="true" name="ConstraintType />
<xs:complexType name="RelConstraint" >
<xs:complexContent>
<xs:extension base="p:ConstraintType">
...
</xs:extension>
<xs:complexContent>
</xs:complexType>
<xs:complexType name="Logic" abstract="true">
<xs:complexContent>
<xs:extension base="p:ConstraintType">
...
</xs:extension>
<xs:complexContent>
</xs:complexType>
<xs:complexType name="AndLogic" >
<xs:complexContent>
<xs:extension base="p:Logic">
...
</xs:extension>
<xs:complexContent>
</xs:complexType>
The element of Constraints is .Item not .Constraint (which is fine and I know that this happens with abstraction).
However, Constraints.Item type is RelConstraint not ConstraintType so it doesn't recognize AndLogic as a possible type.
So it seems that if one concrete type is abstracted one level and another is abstracted two levels the service reference sets any reference to the class only abstracted one level.
(E.g.
ConcreteClassA extends AbstractClassC,
ConcreteClassB extends AbstractClassB which extends AbstractClassC,
ConcreteClassX has element AbstractClassC, which should be of that type.
However, the element is of type ConcreteClassA)
Is there a work around?
This relates to WHY doesn't WCF 'properly' consume/expose abstract types when hosted as a web service
So my first idea that almost worked was removing the abstract="true" from the element declaration but leaving it on the type declaration. Here's the declaration of both the element and type before removing anything:
<xs:element abstract="true" name="Logic" substitutionGroup="p:Constraint" type="p:LogicType" />
<xs:complexType name="LogicType" abstract="true">
<xs:complexContent>
<xs:extension base="p:ConstraintType">
...
</xs:extension>
<xs:complexContent>
</xs:complexType>
Visual studio actually generated the code correctly!
And from what I could find, removing abstract from the element but not the type was not an incorrect implementation of an abstract object. (However, I'm still weary about this point).
So I wrote up some test xml and sent it to my c# program from my server. And of course... it wouldn't Serialize the objects correctly (they were all set to null).
So I finally looked directly at the Reference.cs file to see if I could figure out the problem.
[System.Xml.Serialization.XmlElementAttribute("Logic", typeof(LogicType), Order = 0)]
[System.Xml.Serialization.XmlElementAttribute("RelConstraint", typeof(RelConstraintType), Order = 0)]
public ConstraintComponentType Item
It looked correct but I decided to add XmlElementAttribute declarations for AndLogic and my other logic types to see what happened. And it finally serialized the test data correctly!
[System.Xml.Serialization.XmlElementAttribute("And", typeof(AndType), Order = 0)]
[System.Xml.Serialization.XmlElementAttribute("Logic", typeof(LogicType), Order = 0)]
[System.Xml.Serialization.XmlElementAttribute("RelConstraint", typeof(RelConstraintType), Order = 0)]
public ConstraintComponentType Item
So what I ended up doing was putting abstract="true" back on my elements to make my schema correct, and then going in and doing the aforementioned step along with changing anywhere it incorrectly generated the type, e.g.
[System.Xml.Serialization.XmlElementAttribute("Logic", typeof(LogicType), Order = 0)]
[System.Xml.Serialization.XmlElementAttribute("RelConstraint", typeof(RelConstraintType), Order = 0)]
public RelConstraintType Item
So the only problem is that if you want to regenerate the service reference, you have to go back through and manually edit the Reference.cs file again.

Validation xml to xsd to only catch specific errors

I have an import file that needs to have skip and continue on specific errors. I want to ignore the errors for data type, min/max length, and required fields. I want to catch and display errors about items not formatted correctly and in the wrong location.
In this case the file contains a collection of people.
I want to catch are errors:
1: A Children node outside of a person node.
2: A Child outside of a person node.
3: A Person out side of the people node.
I want to ignore errors:
1: Child does not have a name.
2: Person does not have birth date.
<xs:element name="People">
<xs:complexType>
<xs:sequence>
<xs:element name="Person" minOccurs="1" maxOccurs="unbounded">
<xs:complexType>
<xs:all>
<xs:element name="FirstName" minOccurs="1" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="LastName" minOccurs="1" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="BirthDate" type="Date" minOccurs="1" maxOccurs="1"/>
<xs:element name="Children">
<xs:complexType>
<xs:sequence>
<xs:element name="Child" minOccurs="1" maxOccurs="unbounded">
<xs:complexType>
<xs:all>
<xs:element name="FirstName" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="BirthDate" type="Date" minOccurs="1" maxOccurs="1"/>
</xs:all>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
Change your schema as follows:
In the Firstname element declaration under Child, add an attribute, type="xs:string". Also, remove all the content of the element declaration (simpleType and so on). You can make the tag self-closing if you want.
In the Birthdate element declaration, change minOccurs from 1 to 0.
The first change removes the restriction that's currently placed on the child's name that the content be at least one character long. Adding the type attribute is necessary since you're removing the current definition of the element type.
The second change tells the validator that birth date is not required.
Make those changes and the XML that you want to validate, should.
Whether to stop or carry on when invalid input is presented is, in principle, entirely the consuming software's choice, so what you are describing is logically coherent, though perhaps a bit unusual. If you can get the information you need through the API you're using, no reason not to make the software behave as you describe, and that's probably the preferable option. (But I can't help you with it.)
If you can't get the required information through the API (some APIs do assume that validation is just a yes/no kind of thing), one possible fallback alternative would be to validate using a separate schema weakened as described in ssamuel's answer, so that the only validation errors are the ones you wish to regard as fatal.
That is: there are two ways to solve this problem: (1) move past the idea that you must always abort on invalid input: get the validator to give you more information so you can decide to stop or continue. Or (2) move past the idea that there is a a single schema that applies to all processing of the document: use one schema for document creation and a different schema for deciding what to do with the input.

Deserialize an array of strings with DataContract

I have an xml schema with a type defined like this:
<xs:complexType name="InteractiveWorkflowAddress">
<xs:sequence>
<xs:element name="endpointAddresses" nillable="true" type="q1:ArrayOfstring" xmlns:q1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
<xs:element name="instanceId" type="ser:guid" />
</xs:sequence>
</xs:complexType>
where the ArrayOfstring type is defined as
<xs:complexType name="ArrayOfstring">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="string" nillable="true" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="ArrayOfstring" nillable="true" type="tns:ArrayOfstring" />
How can I deserialize the InteractiveWorkflowAddress type using the DataContract attribute? I tried the following
[DataContract(Namespace = Constants.Namespace)]
public class InteractiveWorkflowAddress {
[DataMember()]
public string[] EndpointAddresses;
[DataMember()]
public string InstanceId;
}
But while the InstanceId member is deserialized correctly, EndpointAddresses is always null.
The best way to figure out how something can be deserialized, is to first serialize. Serialize that InteractiveWorkflowAddress data structure and see the XML is creates. You might be able to List instead of string[] - and also you can control serialization with [XmlArray(...)] and [XmlArrayItem(...)] - which may help too.
The easist way is likely going to be doing some quick trial-and-error, by: making a change, serializing the class, seeing the output - then repeat. Hope that helps!

In .NET XML deserialization, how can I allow polymorphic use of Array types?

Example Schema:
<complexType name="Dog">...</complexType>
<complexType name="Cat">...</complexType>
<complexType name="ArrayOfDog">
<sequence>
<element name="Dog" type="tns:Dog minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="Foo">
<sequence>
<element name="Bar" type="string"/>
<element name="Baz" type="anyType"/>
</sequence>
</complexType>
Running this through .NET's wsdl.exe generates code similar to the following:
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Dog[]))]
public partial class Dog { ... }
public partial class Cat { ... }
public partial class Foo {
private string barField;
private object bazField;
}
It appears that wsdl.exe is trying to be "smart" and realize that my ArrayOfDog is really just a wrapper type that can be encoded as a C# array. This works fine when ArrayOfDog is explicitly referenced in another data type. However, when ArrayOfDog is used polymorphically (e.g. as a substitution for xsd:anyType) this breaks. It appears to break because the .NET runtime knows nothing about the complexType named "ArrayOfDog" - it has basically thrown away this information in favor of just using native C# arrays.
Example XML document 1:
<Foo>
<Bar>Hello</Bar>
<Baz xsi:type="Cat">
...
</Baz>
</Foo>
Example XML document 2:
<Foo>
<Bar>Hello</Bar>
<Baz xsi:type="ArrayOfDog">
<Dog>...</Dog>
<Dog>...</Dog>
</Baz>
</Foo>
Document #1 is deserialized correctly by the runtime. I get an object of type Foo with correctly deserialized fields for Bar and Baz.
Document #2 is deserialized incorrectly by the runtime. I get an object of type Foo with a correctly deserialized field for Bar, but for the Baz field I get System.XML.XMLNode[]. My guess is because the runtime knows nothing about any type binding for an entity named "ArrayOfDog". You might think that the XmlInclude directive "XmlIncludeAttribute(typeof(Dog[]))" would handle this, but it doesn't appear to be working.
Has anyone come across this?
Is there an elegant solution here? The workaround I'm thinking of using is to wrap my "ArrayOf" type in another type and include that in the subsitution for the xsd:anyType.
I don't think this has anything to do with polymorphism. I think this is a bug in the XML Serializer, assuming that any type named "ArrayOfDog", containing a sequence of "Dog" is meant to represent a Dog[]. As a test of this theory, try changing the WSDL to use the name "BunchOfDogs" instead, and see if this changes the proxy code in the client.
If you want polymorphism in XML, then both ArrayOfDog, and Cat, will need to be extensions of the same base type (other than xsd:any). If that were the case, then I'd expect .NET to generate Baz as being of the base type.
Schemas with xsd:any cause problems just in general. There could be almost anything in there, and some combinations will simply not make sense.
You haven't said if this Java service is from Axis, or what version it is. I've seen Axis behave as though xsi:type were a substitute for a valid schema. Be careful with schemas that require "proper" use of xsi:type.
What do you want to start with? If you start with a type defined like this:
public partial class Foo
{
private string _bar;
private object[] _baz;
public string Bar
{
get { return _bar; }
set { _bar= value; }
}
[XmlArray("Baz")]
[XmlArrayItem("Type1", typeof(Type1))]
public object[] Baz
{
get { return _baz; }
set { _baz= value; }
}
}
Then you can serialize and de-serialize documents like your document #2.
Seems like you want to start with the WSDL and XSD. In that case, can you generalize your schema to look something like this:
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Foo" nillable="true" type="Foo" />
<xs:complexType name="Foo">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Bar" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="Baz" type="UntypedArray" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="UntypedArray">
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element name="Type1" type="Type1" minOccurs="1" maxOccurs="1"/>
<xs:any namespace="##other" processContents="lax" minOccurs="1" maxOccurs="1"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="Type1" mixed="true">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Child" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
The above schema generates this code:
public partial class Foo {
private string barField;
private object[] bazField;
/// <remarks/>
public string Bar {
get {
return this.barField;
}
set {
this.barField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlArrayItemAttribute("", typeof(System.Xml.XmlElement), IsNullable=false)]
[System.Xml.Serialization.XmlArrayItemAttribute(typeof(Type1), IsNullable=false)]
public object[] Baz {
get {
return this.bazField;
}
set {
this.bazField = value;
}
}
}
If you want to include other types, then add elements to the the xsd:choice as appropriate.
Since you want to allow xsd:any within it, Foo.Baz is an object array. The programming model on it, requires you to test or cast each element with something like (foo.Baz[i] as Type1).

Categories

Resources