
Question:
I am using xsd.exe to generate some c# classes from a .xsd file. I ran into the same issue that is covered here and on other sites where xsd.exe generates Type[] arrays instead of generic List collections for types in the .xsd file. Some people have suggested that svcutil.exe can be used as a replacement for xsd.exe if you pass the /dataContractOnly parameter to svcutil.exe. However, it seems like those people are mistaken because svcutil.exe actually generates System.Xml.XmlNode[] array properties instead of creating types based on the schema in the .xsd file.
For example, given this simple .xsd schema:
<?xml version="1.0" encoding="utf-8"?> <xs:schema targetNamespace="http://tempuri.org/XMLSchema.xsd" elementFormDefault="qualified" xmlns="http://tempuri.org/XMLSchema.xsd" xmlns:mstns="http://tempuri.org/XMLSchema.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" > <xs:complexType name="Employee"> <xs:all> <xs:element name="FirstName" type="xs:string"></xs:element> <xs:element name="LastName" type="xs:string"></xs:element> </xs:all> </xs:complexType> <xs:element name="Employees"> <xs:complexType> <xs:sequence maxOccurs="unbounded"> <xs:element name="Employee" type="Employee"></xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
'xsd.exe /classes Example.xsd' generates:
public partial class Employees { private Employee[] employeeField; public Employee[] Employee { get { return this.employeeField; } set { this.employeeField = value; } } } public partial class Employee { private string firstNameField; private string lastNameField; public string FirstName { get { return this.firstNameField; } set { this.firstNameField = value; } } public string LastName { get { return this.lastNameField; } set { this.lastNameField = value; } } }
'svcutil.exe /target:code /dataContractOnly /serializer:XmlSerializer /importXmlTypes /collectionType:System.Collections.Generic.List`1 Example.xsd' generates:
public partial class Employee : object, System.Runtime.Serialization.IExtensibleDataObject{ private System.Runtime.Serialization.ExtensionDataObject extensionDataField; private string FirstNameField; private string LastNameField; public System.Runtime.Serialization.ExtensionDataObject ExtensionData{ get{ return this.extensionDataField; } set{ this.extensionDataField = value; } } public string FirstName{ get{ return this.FirstNameField; } set{ this.FirstNameField = value; } } public string LastName{ get{ return this.LastNameField; } set{ this.LastNameField = value; } } } public partial class Employees : object, System.Xml.Serialization.IXmlSerializable{ private System.Xml.XmlNode[] nodesField; private static System.Xml.XmlQualifiedName typeName = new System.Xml.XmlQualifiedName("Employees", "http://tempuri.org/XMLSchema.xsd"); public System.Xml.XmlNode[] Nodes{ get{ return this.nodesField; } set{ this.nodesField = value; } } public void ReadXml(System.Xml.XmlReader reader){ this.nodesField = System.Runtime.Serialization.XmlSerializableServices.ReadNodes(reader); } public void WriteXml(System.Xml.XmlWriter writer){ System.Runtime.Serialization.XmlSerializableServices.WriteNodes(writer, this.Nodes); } public System.Xml.Schema.XmlSchema GetSchema(){ return null; } public static System.Xml.XmlQualifiedName ExportSchema(System.Xml.Schema.XmlSchemaSet schemas){ System.Runtime.Serialization.XmlSerializableServices.AddDefaultSchema(schemas, typeName); return typeName; } }
Is svcutil.exe really supposed to be a replacement for xsd.exe? The output generated seems to be quite different.
At this point, it looks like I will have to use xsd.exe to create classes from my .xsd file and then manually tweak the the code to get it in the form I want. I realize that using purely generated code would be ideal, but I was wondering if other people are using xsd.exe as a starting point and then working from there or if I need to consider another approach altogether?
Are there any updates to xsd.exe in Visual Studio 2010?
Solution:1
Yes, svcutil.exe
can be used as a replacement for xsd.exe
but it sounds like you are having trouble getting generic collections to be generated. svcutil.exe
has a collectionType
switch that allows you to specify the type to be used for a collection:
svcutil /o:Svc.cs /ct:System.Collections.Generic.List`1 http://example.com
Solution:2
Clarification
Andrew Hare's answer above will work, but the example command that jameswelle pasted just above his last section of code:svcutil.exe /target:code /dataContractOnly /serializer:XmlSerializer /importXmlTypes /collectionType:System.Collections.Generic.List`1 Example.xsd
does not work because, as stated on MSDN, '. . .the /r and /ct switches for referencing types are for generating data contracts. These switches do not work when using XmlSerializer.'
HTH.
Solution:3
I would just create your own xsd.exe. Sorry having trouble pasting but if you copy this code into your main:
XmlSchemas xsds = new XmlSchemas(); xsds.Add(xsd); xsds.Compile(null, true); XmlSchemaImporter schemaImporter = new XmlSchemaImporter(xsds); // create the codedom CodeNamespace codeNamespace = new CodeNamespace(strNamespace); XmlCodeExporter codeExporter = new XmlCodeExporter(codeNamespace); List<XmlTypeMapping> maps = new List<XmlTypeMapping>(); foreach (XmlSchemaType schemaType in xsd.SchemaTypes.Values) { maps.Add(schemaImporter.ImportSchemaType(schemaType.QualifiedName)); } foreach (XmlSchemaElement schemaElement in xsd.Elements.Values) { maps.Add(schemaImporter.ImportTypeMapping(schemaElement.QualifiedName)); } foreach (XmlTypeMapping map in maps) { codeExporter.ExportTypeMapping(map); } ReplaceArrayWithList(codeNamespace); // Check for invalid characters in identifiers CodeGenerator.ValidateIdentifiers(codeNamespace); // output the C# code CSharpCodeProvider codeProvider = new CSharpCodeProvider(); using (StreamWriter writer = new StreamWriter(strCsPath, false)) { codeProvider.GenerateCodeFromNamespace(codeNamespace, writer, new CodeGeneratorOptions()); } } private static void ReplaceArrayWithList(CodeNamespace codeNamespace) { codeNamespace.Imports.Add(new CodeNamespaceImport("System.Collections.Generic")); foreach (CodeTypeDeclaration codeType in codeNamespace.Types) { foreach (CodeTypeMember member in codeType.Members) { if (member is CodeMemberField) { CodeMemberField field = (CodeMemberField)member; if (field.Type.ArrayRank > 0) { CodeTypeReference type = new CodeTypeReference(); type.BaseType = "List<" + field.Type.BaseType + ">"; field.Type = type; } } if (member is CodeMemberProperty) { CodeMemberProperty property = (CodeMemberProperty)member; if (property.Type.ArrayRank > 0) { CodeTypeReference type = new CodeTypeReference(); type.BaseType = "List<" + property.Type.BaseType + ">"; property.Type = type; } } } } } }
}
Solution:4
I have tested the same commands on another schema, ang received similar "junk" results from svcutil. So, the might be a way to make it work like xsd.exe, but so far all Ive seen are far less useful ones.
Updated answer: I found that many of these generic arrays of xml nodes were replaced by strong types when all the referenced XSD's are forcibly included. In my case, i have many xsd files all referenced by each other, but svcutil doesnt seem to include them. i had to instead tell it to use *.xsd to get them all.
Solution:5
I have found Xsd2Code to be much better than xsd.exe does does exactly what you need. See here: http://xsd2code.codeplex.com/
Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
EmoticonEmoticon