c# - 如何解析 XSD 以从 <xsd :simpleType> elements using C#? 获取信息

标签 c# c#-4.0 xsd

我有一个包含多个复杂类型和简单类型的 XSD(文件的一部分如下所示)。我需要解析此文档以从复杂类型中引用的每个简单类型中获取 maxLength。任何人都可以就如何实现这个提出一些建议吗?我需要以通用方式实现它,所以如果我查询“Setup_Type”,它应该给出以下输出。谢谢!

NewSetup/Amount = 12(由“/”分隔的元素标签的名称属性和嵌套 simpleType 的 maxLength)

新设置/名称 = 50

<xsd:complexType name="Setup_Type">
  <xsd:sequence>
    <xsd:element name="NewSetup" type="NewSetup_Type" minOccurs="1" maxOccurs="1" />
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="NewSetup_Type">
  <xsd:sequence>
    <xsd:element name="Amount" type="Amount_Type"  minOccurs="1" maxOccurs="1" />
    <xsd:element name="Name" type="Name_Type"  minOccurs="1" maxOccurs="1" />
  </xsd:sequence>
</xsd:complexType>

<xsd:simpleType name="Amount_Type">
  <xsd:annotation>
    <xsd:documentation>Amount</xsd:documentation>
  </xsd:annotation>
  <xsd:restriction base="xsd:string">
    <xsd:maxLength value="12" />
  </xsd:restriction>
</xsd:simpleType>

<xsd:simpleType name="Name_Type">
  <xsd:annotation>
    <xsd:documentation>Name</xsd:documentation>
  </xsd:annotation>
  <xsd:restriction base="xsd:string">
    <xsd:maxLength value="50" />
  </xsd:restriction>
</xsd:simpleType>

最佳答案

我在过去看到过类似的问题(完全公开,我自己也问过类似的问题 question)。解析 XSD 不适合胆小的人。

您基本上有 2 个选项,第一个更容易实现,但可以通过对 XSD 进行较小的更改来更容易地破坏。第二个更强大但难以实现。

选项 1:

使用 LINQ(如果您愿意,也可以使用其他 C# XML 解析器)解析 XSD。由于 XSD 只是一个 XML,您可以将其加载到 XDocument 中并通过 LINQ 读取它。

您自己的 XSD 示例:

<xsd:simpleType name="Amount_Type">
  <xsd:annotation>
    <xsd:documentation>Amount</xsd:documentation>
  </xsd:annotation>
  <xsd:restriction base="xsd:string">
    <xsd:maxLength value="12" />
  </xsd:restriction>
</xsd:simpleType>

您可以访问 MaxLength:

var xDoc = XDocument.Load("your XSD path");
var ns = XNamespace.Get(@"http://www.w3.org/2001/XMLSchema");

var length = (from sType in xDoc.Element(ns + "schema").Elements(ns + "simpleType")
              where sType.Attribute("name").Value == "Amount_Type"
              from r in sType.Elements(ns + "restriction")
              select r.Element(ns + "maxLength").Attribute("value")
                      .Value).FirstOrDefault();

这并没有提供一种非常简单的方法来按类型名称进行解析,尤其是对于扩展类型。要使用它,您需要知道要查找的每个元素的确切路径。

选项 2:

这太复杂了,无法快速回答(注意:请参阅下面的编辑 - 我花了一些时间整理了一个可行的解决方案),所以我鼓励您看看我的我在上面链接的自己的问题。在其中,我链接了一个 great blog它显示了如何认真地将 XSD 分解成多个部分,并可能允许您执行所需的搜索类型。您必须决定开发它是否值得(该博客显示了使用 XmlReader 的实现,其中包含针对相关 XSD 进行验证的 XML,但您可以通过直接加载来轻松完成此操作XSD 并对其进行解析。

在博客中找到的 2 个关键想法是:

// in the getRestriction method (reader in this context is an `XmlReader` that 
//  contains a XML that is being validated against the specific XSD
if (reader.SchemaInfo.SchemaElement == null) return null;
simpleType = reader.SchemaInfo.SchemaElement.ElementSchemaType as XmlSchemaSimpleType;
if (simpleType == null) return null;
restriction = simpleType.Content as XmlSchemaSimpleTypeRestriction;

// then in the getMaxLength method
if (restriction == null) return null;
List<int> result = new List<int>();
foreach (XmlSchemaObject facet in restriction.Facets) {
if (facet is XmlSchemaMaxLengthFacet) result.Add(int.Parse(((XmlSchemaFacet) facet).Value));

去年我实际上尝试过同样的事情来解析 XSD 作为复杂数据验证方法的一部分。我花了一周的大部分时间才真正了解发生了什么,并根据我的目的调整博客中的方法。这绝对是实现您想要的东西的最佳方式。

如果您想尝试使用独立模式,您可以将 XSD 加载到 XmlSchemaSet 对象中,然后使用 GlobalTypes 属性来帮助您找到特定类型你正在寻找。


编辑: 我提取了我的旧代码并开始整理这些代码来帮助您。

首先加载您的架构:

XmlSchemaSet set; // this needs to be accessible to the methods below,
                  //  so should be a class level field or property

using (var fs = new FileStream(@"your path here", FileMode.Open)
{
    var schema = XmlSchema.Read(fs, null);

    set = new XmlSchemaSet();
    set.Add(schema);
    set.Compile();
}

根据您提供的 XSD,以下方法应该可以让您接近您想要的结果。它应该非常适合处理更复杂的结构。

public Dictionary<string, int> GetElementMaxLength(String xsdElementName)
{
    if (xsdElementName == null) throw new ArgumentException();
    // if your XSD has a target namespace, you need to replace null with the namespace name
    var qname = new XmlQualifiedName(xsdElementName, null);

    // find the type you want in the XmlSchemaSet    
    var parentType = set.GlobalTypes[qname];

    // call GetAllMaxLength with the parentType as parameter
    var results = GetAllMaxLength(parentType);

    return results;
}

private Dictionary<string, int> GetAllMaxLength(XmlSchemaObject obj)
{
    Dictionary<string, int> dict = new Dictionary<string, int>();

    // do some type checking on the XmlSchemaObject
    if (obj is XmlSchemaSimpleType)
    {
        // if it is a simple type, then call GetMaxLength to get the MaxLength restriction
        var st = obj as XmlSchemaSimpleType;
        dict[st.QualifiedName.Name] = GetMaxLength(st);
    }
    else if (obj is XmlSchemaComplexType)
    {

        // if obj is a complexType, cast the particle type to a sequence
        //  and iterate the sequence
        //  warning - this will fail if it is not a sequence, so you might need
        //  to make some adjustments if you have something other than a xs:sequence
        var ct = obj as XmlSchemaComplexType;
        var seq = ct.ContentTypeParticle as XmlSchemaSequence;

        foreach (var item in seq.Items)
        {
            // item will be an XmlSchemaObject, so just call this same method
            //  with item as the parameter to parse it out
            var rng = GetAllMaxLength(item);

            // add the results to the dictionary
            foreach (var kvp in rng)
            {
                dict[kvp.Key] = kvp.Value;
            }
        }
    }
    else if (obj is XmlSchemaElement)
    {
        // if obj is an XmlSchemaElement, the you need to find the type
        //  based on the SchemaTypeName property.  This is why your 
        //  XmlSchemaSet needs to have class-level scope
        var ele = obj as XmlSchemaElement;
        var type = set.GlobalTypes[ele.SchemaTypeName];

        // once you have the type, call this method again and get the dictionary result
        var rng = GetAllMaxLength(type);

        // put the results in this dictionary.  The difference here is the dictionary
        //  key is put in the format you specified
        foreach (var kvp in rng)
        {
            dict[String.Format("{0}/{1}", ele.QualifiedName.Name, kvp.Key)] = kvp.Value;
        }
    }

    return dict;
}

private Int32 GetMaxLength(XmlSchemaSimpleType xsdSimpleType)
{
    // get the content of the simple type
    var restriction = xsdSimpleType.Content as XmlSchemaSimpleTypeRestriction;

    // if it is null, then there are no restrictions and return -1 as a marker value
    if (restriction == null) return -1;

    Int32 result = -1;

    // iterate the facets in the restrictions, look for a MaxLengthFacet and parse the value
    foreach (XmlSchemaObject facet in restriction.Facets)
    {
        if (facet is XmlSchemaMaxLengthFacet)
        {
            result = int.Parse(((XmlSchemaFacet)facet).Value);
            break;
        }
    }

    return result;
}

那么用法就很简单了,你只需要调用 GetElementMaxLength(String) 方法,它就会返回你提供的格式的名字字典,最大长度为值:

var results = GetElementMaxLength("Setup_Type");

foreach (var item in results)
{
    Console.WriteLine("{0} | {1}", item.Key, item.Value);                
}

关于c# - 如何解析 XSD 以从 <xsd :simpleType> elements using C#? 获取信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11569264/

相关文章:

c# - 如何增加长时间运行的查询的 executionTimeout?

c# - 如何避免 Controller 中每个 Action 发生的重复代码

c# - 放弃重载以支持扩展方法

xsd - 仅当设置了另一个属性时才需要一个属性

java - 除了 XSOM 之外,还有其他框架可以解析 XSD 吗?

c# - Entity Framework 更新操作 - 为什么首先更新子记录?

c# - 绑定(bind)到 WPF 静态类中的静态属性

windows - 如何从其他应用程序读取屏幕内容 [Office Communicator]

c# - 我在 SharpDevelop 上这个非常简单的程序中犯了什么错误?

ios - 在 iOS 中操作 XML 文件