लेख Writing your own XSD.exe माइक Hadlow द्वारा xsd.exe
के अपने स्वयं के संस्करण बनाने के लिए एक बुनियादी ढांचा देता है।
आयात XmlSchema.Read()
और XmlSchemaImporter
साथ स्कीमा: यह निम्नलिखित चरण हैं।
XmlCodeExporter
का उपयोग करने के लिए नेट प्रकार (ओं) और विशेषता उत्पन्न करें।
जेनरेट किए गए प्रकार और विशेषताओं को वांछित के रूप में ट्विक करें।
यहाँ आप उत्पन्न xxxSpecified
गुण को निकालने और उनके इसी 'असली' गुण को बढ़ावा देने के नल के लिए चाहते हैं।
CSharpCodeProvider
का उपयोग कर अंतिम कोड जनरेट करें।
इस ढांचे का उपयोग करना, और प्रयोगात्मक निर्धारित करने XmlCodeExporter
द्वारा किस प्रकार वास्तव में उत्पन्न कर रहे हैं एक डिबगर का उपयोग करके, मैं बनाया निम्नलिखित CustomXsdCodeGenerator
:
public class CustomXsdCodeGenerator : CustomXsdCodeGeneratorBase
{
readonly bool promoteToNullable;
public CustomXsdCodeGenerator(string Namespace, bool promoteToNullable) : base(Namespace)
{
this.promoteToNullable = promoteToNullable;
}
protected override void ModifyGeneratedCodeTypeDeclaration(CodeTypeDeclaration codeType, CodeNamespace codeNamespace)
{
RemoveSpecifiedProperties(codeNamespace, promoteToNullable);
base.ModifyGeneratedCodeTypeDeclaration(codeType, codeNamespace);
}
private static void RemoveSpecifiedProperties(CodeNamespace codeNamespace, bool promoteToNullable)
{
foreach (CodeTypeDeclaration codeType in codeNamespace.Types)
{
RemoveSpecifiedProperties(codeType, codeNamespace, promoteToNullable);
}
}
private static void RemoveSpecifiedProperties(CodeTypeDeclaration codeType, CodeNamespace codeNamespace, bool promoteToNullable)
{
var toRemove = new List<CodeTypeMember>();
foreach (var property in codeType.Members.OfType<CodeMemberProperty>())
{
CodeMemberField backingField;
CodeMemberProperty specifiedProperty;
if (!property.TryGetBackingFieldAndSpecifiedProperty(codeType, out backingField, out specifiedProperty))
continue;
var specifiedField = specifiedProperty.GetBackingField(codeType);
if (specifiedField == null)
continue;
toRemove.Add(specifiedProperty);
toRemove.Add(specifiedField);
if (promoteToNullable)
{
// Do not do this for attributes
if (property.CustomAttributes.Cast<CodeAttributeDeclaration>().Any(a => a.AttributeType.BaseType == typeof(System.Xml.Serialization.XmlAttributeAttribute).FullName))
continue;
var typeRef = property.Type;
if (typeRef.ArrayRank > 0)
// An array - not a reference type.
continue;
// OK, two possibilities here:
// 1) The property might reference some system type such as DateTime or decimal
// 2) The property might reference some type being defined such as an enum or struct.
var type = Type.GetType(typeRef.BaseType);
if (type != null)
{
if (!type.IsClass)
{
if (type == typeof(Nullable<>))
// Already nullable
continue;
else if (!type.IsGenericTypeDefinition && (type.IsValueType || type.IsEnum) && Nullable.GetUnderlyingType(type) == null)
{
var nullableType = typeof(Nullable<>).MakeGenericType(type);
var newRefType = new CodeTypeReference(nullableType);
property.Type = newRefType;
backingField.Type = newRefType;
}
}
}
else
{
var generatedType = codeNamespace.FindCodeType(typeRef);
if (generatedType != null)
{
if (generatedType.IsStruct || generatedType.IsEnum)
{
var newRefType = new CodeTypeReference(typeof(Nullable<>).FullName, typeRef);
property.Type = newRefType;
backingField.Type = newRefType;
}
}
}
}
}
foreach (var member in toRemove)
{
codeType.Members.Remove(member);
}
}
}
public static class CodeNamespaceExtensions
{
public static CodeTypeDeclaration FindCodeType(this CodeNamespace codeNamespace, CodeTypeReference reference)
{
if (codeNamespace == null)
throw new ArgumentNullException();
if (reference == null)
return null;
CodeTypeDeclaration foundType = null;
foreach (CodeTypeDeclaration codeType in codeNamespace.Types)
{
if (codeType.Name == reference.BaseType)
{
if (foundType == null)
foundType = codeType;
else if (foundType != codeType)
{
foundType = null;
break;
}
}
}
return foundType;
}
}
public static class CodeMemberPropertyExtensions
{
public static bool TryGetBackingFieldAndSpecifiedProperty(this CodeMemberProperty property, CodeTypeDeclaration codeType,
out CodeMemberField backingField, out CodeMemberProperty specifiedProperty)
{
if (property == null)
{
backingField = null;
specifiedProperty = null;
return false;
}
if ((backingField = property.GetBackingField(codeType)) == null)
{
specifiedProperty = null;
return false;
}
specifiedProperty = null;
var specifiedName = property.Name + "Specified";
foreach (var p in codeType.Members.OfType<CodeMemberProperty>())
{
if (p.Name == specifiedName)
{
// Make sure the property is marked as XmlIgnore (there might be a legitimate, serializable property
// named xxxSpecified).
if (!p.CustomAttributes.Cast<CodeAttributeDeclaration>().Any(a => a.AttributeType.BaseType == typeof(System.Xml.Serialization.XmlIgnoreAttribute).FullName))
continue;
if (specifiedProperty == null)
specifiedProperty = p;
else if (specifiedProperty != p)
{
specifiedProperty = null;
break;
}
}
}
if (specifiedProperty == null)
return false;
if (specifiedProperty.GetBackingField(codeType) == null)
return false;
return true;
}
public static CodeMemberField GetBackingField(this CodeMemberProperty property, CodeTypeDeclaration codeType)
{
if (property == null)
return null;
CodeMemberField returnedField = null;
foreach (var statement in property.GetStatements.OfType<CodeMethodReturnStatement>())
{
var expression = statement.Expression as CodeFieldReferenceExpression;
if (expression == null)
return null;
if (!(expression.TargetObject is CodeThisReferenceExpression))
return null;
var fieldName = expression.FieldName;
foreach (var field in codeType.Members.OfType<CodeMemberField>())
{
if (field.Name == fieldName)
{
if (returnedField == null)
returnedField = field;
else if (returnedField != field)
return null;
}
}
}
return returnedField;
}
}
public abstract class CustomXsdCodeGeneratorBase
{
// This base class adapted from http://mikehadlow.blogspot.com/2007/01/writing-your-own-xsdexe.html
readonly string Namespace;
public CustomXsdCodeGeneratorBase(string Namespace)
{
this.Namespace = Namespace;
}
public void XsdToClassTest(IEnumerable<string> xsds, TextWriter codeWriter)
{
XsdToClassTest(xsds.Select(xsd => (Func<TextReader>)(() => new StringReader(xsd))), codeWriter);
}
public void XsdToClassTest(IEnumerable<Func<TextReader>> xsds, TextWriter codeWriter)
{
var schemas = new XmlSchemas();
foreach (var getReader in xsds)
{
using (var reader = getReader())
{
var xsd = XmlSchema.Read(reader, null);
schemas.Add(xsd);
}
}
schemas.Compile(null, true);
var schemaImporter = new XmlSchemaImporter(schemas);
var maps = new List<XmlTypeMapping>();
foreach (XmlSchema xsd in schemas)
{
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));
}
}
// create the codedom
var codeNamespace = new CodeNamespace(this.Namespace);
var codeExporter = new XmlCodeExporter(codeNamespace);
foreach (XmlTypeMapping map in maps)
{
codeExporter.ExportTypeMapping(map);
}
ModifyGeneratedNamespace(codeNamespace);
// Check for invalid characters in identifiers
CodeGenerator.ValidateIdentifiers(codeNamespace);
// output the C# code
var codeProvider = new CSharpCodeProvider();
codeProvider.GenerateCodeFromNamespace(codeNamespace, codeWriter, new CodeGeneratorOptions());
}
protected virtual void ModifyGeneratedNamespace(CodeNamespace codeNamespace)
{
foreach (CodeTypeDeclaration codeType in codeNamespace.Types)
{
ModifyGeneratedCodeTypeDeclaration(codeType, codeNamespace);
}
}
protected virtual void ModifyGeneratedCodeTypeDeclaration(CodeTypeDeclaration codeType, CodeNamespace codeNamespace)
{
}
}
यह परीक्षण करने के लिए, मैं निम्नलिखित प्रकार बनाया:
namespace SampleClasses
{
public class SimleSampleClass
{
[XmlElement]
public decimal Something { get; set; }
[XmlIgnore]
public bool SomethingSpecified { get; set; }
}
[XmlRoot("RootClass")]
public class RootClass
{
[XmlArray]
[XmlArrayItem("SampleClass")]
public List<SampleClass> SampleClasses { get; set; }
}
[XmlRoot("SampleClass")]
public class SampleClass
{
[XmlAttribute]
public long Id { get; set; }
public decimal Something { get; set; }
[XmlIgnore]
public bool SomethingSpecified { get; set; }
public SomeEnum SomeEnum { get; set; }
[XmlIgnore]
public bool SomeEnumSpecified { get; set; }
public string SomeString { get; set; }
[XmlIgnore]
public bool SomeStringSpecified { get; set; }
public decimal? SomeNullable { get; set; }
[XmlIgnore]
public bool SomeNullableSpecified { get; set; }
public DateTime SomeDateTime { get; set; }
[XmlIgnore]
public bool SomeDateTimeSpecified { get; set; }
// https://stackoverflow.com/questions/3280362/most-elegant-xml-serialization-of-color-structure
[XmlElement(Type = typeof(XmlColor))]
public Color MyColor { get; set; }
[XmlIgnore]
public bool MyColorSpecified { get; set; }
}
public enum SomeEnum
{
DefaultValue,
FirstValue,
SecondValue,
ThirdValue,
}
// https://stackoverflow.com/questions/3280362/most-elegant-xml-serialization-of-color-structure
public struct XmlColor
{
private Color? color_;
private Color Color
{
get
{
return color_ ?? Color.Black;
}
set
{
color_ = value;
}
}
public XmlColor(Color c) { color_ = c; }
public Color ToColor()
{
return Color;
}
public void FromColor(Color c)
{
Color = c;
}
public static implicit operator Color(XmlColor x)
{
return x.ToColor();
}
public static implicit operator XmlColor(Color c)
{
return new XmlColor(c);
}
[XmlAttribute]
public string Web
{
get { return ColorTranslator.ToHtml(Color); }
set
{
try
{
if (Alpha == 0xFF) // preserve named color value if possible
Color = ColorTranslator.FromHtml(value);
else
Color = Color.FromArgb(Alpha, ColorTranslator.FromHtml(value));
}
catch (Exception)
{
Color = Color.Black;
}
}
}
[XmlAttribute]
public byte Alpha
{
get { return Color.A; }
set
{
if (value != Color.A) // avoid hammering named color if no alpha change
Color = Color.FromArgb(value, Color);
}
}
public bool ShouldSerializeAlpha() { return Alpha < 0xFF; }
}
}
जेनेरिक xsd.exe
का उपयोग करके मैंने निम्न स्कीमा उत्पन्न किया:
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="SimleSampleClass" nillable="true" type="SimleSampleClass" />
<xs:complexType name="SimleSampleClass">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Something" type="xs:decimal" />
</xs:sequence>
</xs:complexType>
<xs:element name="RootClass" nillable="true" type="RootClass" />
<xs:complexType name="RootClass">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="SampleClasses" type="ArrayOfSampleClass" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="ArrayOfSampleClass">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="SampleClass" nillable="true" type="SampleClass" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="SampleClass">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Something" type="xs:decimal" />
<xs:element minOccurs="0" maxOccurs="1" name="SomeEnum" type="SomeEnum" />
<xs:element minOccurs="0" maxOccurs="1" name="SomeString" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="SomeNullable" nillable="true" type="xs:decimal" />
<xs:element minOccurs="0" maxOccurs="1" name="SomeDateTime" type="xs:dateTime" />
<xs:element minOccurs="0" maxOccurs="1" name="MyColor" type="XmlColor" />
</xs:sequence>
<xs:attribute name="Id" type="xs:long" use="required" />
</xs:complexType>
<xs:simpleType name="SomeEnum">
<xs:restriction base="xs:string">
<xs:enumeration value="DefaultValue" />
<xs:enumeration value="FirstValue" />
<xs:enumeration value="SecondValue" />
<xs:enumeration value="ThirdValue" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="XmlColor">
<xs:attribute name="Web" type="xs:string" />
<xs:attribute name="Alpha" type="xs:unsignedByte" />
</xs:complexType>
<xs:element name="SampleClass" nillable="true" type="SampleClass" />
<xs:element name="SomeEnum" type="SomeEnum" />
<xs:element name="XmlColor" type="XmlColor" />
</xs:schema>
और, इस स्कीमा का उपयोग कर, मैं CustomXsdCodeGenerator
का उपयोग कर निम्नलिखित सी # वर्गों promoteToNullable = true
और Namespace = "Question42295155"
साथ पुनर्जीवित:
namespace Question42295155 {
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("XsdToClassTest", "1.0.0.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=true)]
public partial class SimleSampleClass {
private System.Nullable<decimal> somethingField;
/// <remarks/>
public System.Nullable<decimal> Something {
get {
return this.somethingField;
}
set {
this.somethingField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("XsdToClassTest", "1.0.0.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=true)]
public partial class SampleClass {
private System.Nullable<decimal> somethingField;
private System.Nullable<SomeEnum> someEnumField;
private string someStringField;
private System.Nullable<decimal> someNullableField;
private System.Nullable<System.DateTime> someDateTimeField;
private XmlColor myColorField;
private long idField;
/// <remarks/>
public System.Nullable<decimal> Something {
get {
return this.somethingField;
}
set {
this.somethingField = value;
}
}
/// <remarks/>
public System.Nullable<SomeEnum> SomeEnum {
get {
return this.someEnumField;
}
set {
this.someEnumField = value;
}
}
/// <remarks/>
public string SomeString {
get {
return this.someStringField;
}
set {
this.someStringField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public System.Nullable<decimal> SomeNullable {
get {
return this.someNullableField;
}
set {
this.someNullableField = value;
}
}
/// <remarks/>
public System.Nullable<System.DateTime> SomeDateTime {
get {
return this.someDateTimeField;
}
set {
this.someDateTimeField = value;
}
}
/// <remarks/>
public XmlColor MyColor {
get {
return this.myColorField;
}
set {
this.myColorField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public long Id {
get {
return this.idField;
}
set {
this.idField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("XsdToClassTest", "1.0.0.0")]
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)]
public enum SomeEnum {
/// <remarks/>
DefaultValue,
/// <remarks/>
FirstValue,
/// <remarks/>
SecondValue,
/// <remarks/>
ThirdValue,
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("XsdToClassTest", "1.0.0.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=true)]
public partial class XmlColor {
private string webField;
private byte alphaField;
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string Web {
get {
return this.webField;
}
set {
this.webField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public byte Alpha {
get {
return this.alphaField;
}
set {
this.alphaField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("XsdToClassTest", "1.0.0.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=true)]
public partial class RootClass {
private SampleClass[] sampleClassesField;
/// <remarks/>
public SampleClass[] SampleClasses {
get {
return this.sampleClassesField;
}
set {
this.sampleClassesField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("XsdToClassTest", "1.0.0.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=true)]
public partial class ArrayOfSampleClass {
private SampleClass[] sampleClassField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("SampleClass", IsNullable=true)]
public SampleClass[] SampleClass {
get {
return this.sampleClassField;
}
set {
this.sampleClassField = value;
}
}
}
}
सूचना है कि:
नाम में समाप्त होने के कोई गुण हैं Specified
।
गुण Something
, SomeEnum
और SomeDateTime
शून्य हो गए हैं।
पहले से ही-व्यर्थ public decimal? SomeNullable { get; set; }
public System.Nullable<decimal> SomeNullable
को बल्कि कुछ भयानक डबल-व्यर्थ System.Nullable<System.Nullable<decimal>>
बनने से नाकाम रहने से राउंड ट्रिप।
मैं तो प्रारंभिक RootClass
से निम्न XML उत्पन्न:
<RootClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SampleClasses>
<SampleClass Id="10101">
<Something>2.718</Something>
<SomeEnum>ThirdValue</SomeEnum>
<SomeString>hello</SomeString>
<SomeNullable>3.14</SomeNullable>
<SomeDateTime>2017-02-28T00:00:00-05:00</SomeDateTime>
<MyColor Web="Maroon" />
</SampleClass>
</SampleClasses>
</RootClass>
और सफलतापूर्वक डेटा हानि के बिना उत्पन्न वर्ग Question42295155.RootClass
को यह deserialize करने में सक्षम था।
नोट - यह कोड हल्के ढंग से परीक्षण किया गया है। यदि आप एक प्रदान करना चाहते हैं तो मैं एक नमूना स्कीमा के साथ पुनः परीक्षण कर सकता हूं।
अधिक जानकारी के लिए, Code Generation in the .NET Framework Using XML Schema देखें।
Xsd2Code उपकरण आज़माएं। मैंने xsd.exe के बजाय इसे बड़े पैमाने पर उपयोग किया। दुर्भाग्य से मैं आपको यह बताने के लिए नहीं कह सकता कि क्या उस विशिष्ट समस्या का उल्लेख आप हल कर चुके हैं। – Evk
क्या आपने न्यूनतम/अधिकतम होने के बजाय 'nillable = 'true' का उपयोग करने का प्रयास किया है? – tchrikch
@Evk: Xsd2Code हुड के नीचे XmlCodeExporter का उपयोग करने लगता है और ऐसा लगता है कि एक ही समस्या है। – JacquesB