Writing your own XSD.exe Mike Hadlow가 작성한 xsd.exe
의 고유 한 버전을 만드는 데 필요한 기본 프레임 워크를 제공합니다. 그것은 다음 단계가 있습니다 XmlSchema.Read()
와 XmlSchemaImporter
와
가져 오기 스키마를.
XmlCodeExporter
을 사용하여 .Net 유형 및 속성을 생성하십시오.
원하는대로 생성 된 유형 및 속성을 조정하십시오..
여기서 생성 된 xxxSpecified
속성을 제거하고 해당 "실제"속성을 null로 승격 할 수 있습니다.
CSharpCodeProvider
을 사용하여 최종 코드를 생성하십시오.
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)
{
}
}
그것을 테스트하려면, 나는 다음과 같은 유형의 생성 :
이 프레임 워크를 사용하고, 실험적으로 디버거를 사용 XmlCodeExporter
에 의해 종류가 실제로 생성되는 것을 결정함으로써, 나는이 CustomXsdCodeGenerator
다음 생성
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>
는 그리고,이 스키마를 사용하여, 나는 promoteToNullable = true
과 Namespace = "Question42295155"
와 CustomXsdCodeGenerator
를 사용하여 다음 C# 클래스를 재생 :
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
은 Nullable이되었습니다.
오히려 약간 무서운 이중 널 (NULL) System.Nullable<System.Nullable<decimal>>
됨으로써 실패보다 더 public System.Nullable<decimal> SomeNullable
에 이미 널 (NULL) public decimal? SomeNullable { get; set; }
왕복.
나는 다음 초기
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
에 역 직렬화 할 수 있었다.
참고 -이 코드는 가볍게 테스트되었습니다. 샘플 스키마를 제공하려는 경우 샘플 스키마로 다시 테스트 할 수 있습니다.
자세한 내용은 Code Generation in the .NET Framework Using XML Schema을 참조하십시오.
Xsd2Code 도구를 사용해보십시오. xsd.exe 대신 광범위하게 사용했습니다. 불행히도 내가 말한 구체적인 문제가 해결되었는지 확실하게 말할 수는 없습니다. – Evk
min/max 대신'nillable = 'true'를 사용 해봤습니까? – tchrikch
@Evk : Xsd2Code는 XmlCodeExporter를 사용하는 것으로 보이며 같은 문제가있는 것 같습니다. – JacquesB