You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XSD 1.0能否按属性值区分同名Parent元素的类型?

问题描述

需要为以下XML文件编写符合XSD 1.0规范的Schema(用于客户生成XML,我方校验),要求Parent元素的类型由DeterminingAttribute属性值决定。使用环境为.NET Framework 4.7.2,核心工具如XmlReader不支持XSD 1.1,且不能使用商业插件。

目标XML示例

<?xml version="1.0" encoding="utf-8"?>
<Wrapper>
  <Parent Id="1" DeterminingAttribute="A">
    <Data>
      <base1></base1>
      <base2></base2>
      <A1></A1>
      <A2></A2>
    </Data>
  </Parent>
  <Parent Id="2" DeterminingAttribute="B">
    <Data>
      <base1></base1>
      <base2></base2>
      <B1></B1>
      <B2></B2>
    </Data>
  </Parent>
</Wrapper>

(注:原始XML中第二个Parent标签拼写错误为Parrent,需修正为Parent才能通过基础校验)

现有XSD及报错

尝试编写的XSD如下,Visual Studio显示格式合法,但代码校验时抛出错误:提示Parent元素的父元素需包含至少一个元素,且Data元素不合法。

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Wrapper">
    <xs:complexType>
      <xs:sequence minOccurs="0">
        <xs:element minOccurs="0" name="Parent" type="Parent-base"></xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:complexType name="Parent-base" abstract="true">
    <xs:attribute name="Id" type="xs:unsignedInt"></xs:attribute>
  </xs:complexType>

  <xs:complexType name="Parent-spec-A">
    <xs:complexContent>
      <xs:extension base="Parent-base">
        <xs:sequence>
          <xs:element name="Data" type="Data-spec-A"></xs:element>
        </xs:sequence>
        <xs:attribute name="DeterminingAttribute" type="xs:string" fixed="A"></xs:attribute>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

  <xs:complexType name="Parent-spec-B">
    <xs:complexContent>
      <xs:extension base="Parent-base">
        <xs:sequence>
          <xs:element name="Data" type="Data-spec-B"></xs:element>
        </xs:sequence>
        <xs:attribute name="DeterminingAttribute" type="xs:string" fixed="B"></xs:attribute>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

  <xs:complexType name="Data-spec-A">
    <xs:sequence>
      <xs:element name="base1" type="xs:unsignedByte" />
      <xs:element name="base2" type="xs:string" />
      <xs:element name="A1" type="xs:unsignedShort" />
      <xs:element name="A2" type="xs:unsignedShort" />
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="Data-spec-B">
    <xs:sequence>
      <xs:element name="base1" type="xs:unsignedByte" />
      <xs:element name="base2" type="xs:string" />
      <xs:element name="B1" type="xs:unsignedShort" />
      <xs:element name="B2" type="xs:unsignedShort" />
    </xs:sequence>
  </xs:complexType>

</xs:schema>
解决方案

XSD 1.0不支持条件类型赋值(即根据属性值动态绑定元素类型,这是XSD 1.1的特性),所以需要调整设计,以下是两种可行方案:

方案1:修改XML结构,使用替换组(Substitution Groups)

这种方案需要调整XML中Parent元素的命名,让不同类型的Parent使用不同标签名,通过XSD替换组实现类型约束,同时保留原有的属性逻辑。

调整后的XML示例

<?xml version="1.0" encoding="utf-8"?>
<Wrapper>
  <ParentA Id="1" DeterminingAttribute="A">
    <Data>
      <base1></base1>
      <base2></base2>
      <A1></A1>
      <A2></A2>
    </Data>
  </ParentA>
  <ParentB Id="2" DeterminingAttribute="B">
    <Data>
      <base1></base1>
      <base2></base2>
      <B1></B1>
      <B2></B2>
    </Data>
  </ParentB>
</Wrapper>

对应的XSD

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <!-- 定义抽象的Parent元素作为替换组的头 -->
  <xs:element name="Parent" abstract="true"/>

  <!-- Wrapper元素定义:接受任意数量的Parent替换组成员 -->
  <xs:element name="Wrapper">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="Parent" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <!-- 基础Parent类型 -->
  <xs:complexType name="Parent-base">
    <xs:sequence>
      <xs:element name="Data" type="Data-base"/>
    </xs:sequence>
    <xs:attribute name="Id" type="xs:unsignedInt" use="required"/>
    <xs:attribute name="DeterminingAttribute" type="xs:string" use="required"/>
  </xs:complexType>

  <!-- 基础Data类型:包含公共元素base1、base2 -->
  <xs:complexType name="Data-base">
    <xs:sequence>
      <xs:element name="base1" type="xs:unsignedByte" nillable="true"/>
      <xs:element name="base2" type="xs:string" nillable="true"/>
      <!-- 预留扩展点,用于不同类型的Data添加专属元素 -->
      <xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
    </xs:sequence>
  </xs:complexType>

  <!-- ParentA的具体类型 -->
  <xs:element name="ParentA" substitutionGroup="Parent">
    <xs:complexType>
      <xs:complexContent>
        <xs:extension base="Parent-base">
          <xs:sequence>
            <xs:element name="Data">
              <xs:complexType>
                <xs:complexContent>
                  <xs:extension base="Data-base">
                    <xs:sequence>
                      <xs:element name="A1" type="xs:unsignedShort" nillable="true"/>
                      <xs:element name="A2" type="xs:unsignedShort" nillable="true"/>
                    </xs:sequence>
                  </xs:extension>
                </xs:complexContent>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
          <xs:attribute name="DeterminingAttribute" fixed="A"/>
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>

  <!-- ParentB的具体类型 -->
  <xs:element name="ParentB" substitutionGroup="Parent">
    <xs:complexType>
      <xs:complexContent>
        <xs:extension base="Parent-base">
          <xs:sequence>
            <xs:element name="Data">
              <xs:complexType>
                <xs:complexContent>
                  <xs:extension base="Data-base">
                    <xs:sequence>
                      <xs:element name="B1" type="xs:unsignedShort" nillable="true"/>
                      <xs:element name="B2" type="xs:unsignedShort" nillable="true"/>
                    </xs:sequence>
                  </xs:extension>
                </xs:complexContent>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
          <xs:attribute name="DeterminingAttribute" fixed="B"/>
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>
</xs:schema>

说明:

  • 使用substitutionGroup定义可替换的Parent元素,ParentA和ParentB都属于Parent替换组,能被Wrapper接受
  • 为Data定义基础类型,再分别扩展出对应A、B的专属元素
  • 设置nillable="true"允许元素为空(匹配原始XML中空元素的情况)
  • DeterminingAttribute设置固定值,确保类型与属性值一致

方案2:保留原XML结构,宽松XSD配合自定义校验

如果无法修改XML结构,只能先编写一个宽松的XSD覆盖所有可能的情况,再通过.NET代码添加自定义逻辑校验DeterminingAttribute与Data子元素的对应关系。

宽松版XSD

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Wrapper">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Parent" minOccurs="0" maxOccurs="unbounded">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="Data">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="base1" type="xs:unsignedByte" nillable="true"/>
                    <xs:element name="base2" type="xs:string" nillable="true"/>
                    <!-- 允许A1/A2或B1/B2出现,通过代码校验互斥 -->
                    <xs:choice minOccurs="0">
                      <xs:sequence>
                        <xs:element name="A1" type="xs:unsignedShort" nillable="true"/>
                        <xs:element name="A2" type="xs:unsignedShort" nillable="true"/>
                      </xs:sequence>
                      <xs:sequence>
                        <xs:element name="B1" type="xs:unsignedShort" nillable="true"/>
                        <xs:element name="B2" type="xs:unsignedShort" nillable="true"/>
                      </xs:sequence>
                    </xs:choice>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="Id" type="xs:unsignedInt" use="required"/>
            <xs:attribute name="DeterminingAttribute" type="xs:string" use="required"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

自定义校验代码示例(C#)

using System;
using System.Xml;
using System.Xml.Schema;

public class XmlValidator
{
    public static void ValidateXml(string xmlPath, string xsdPath)
    {
        XmlSchemaSet schemaSet = new XmlSchemaSet();
        schemaSet.Add("", xsdPath);

        XmlReaderSettings settings = new XmlReaderSettings();
        settings.ValidationType = ValidationType.Schema;
        settings.Schemas = schemaSet;
        settings.ValidationEventHandler += ValidationEventHandler;

        // 先通过XSD基础校验
        using (XmlReader reader = XmlReader.Create(xmlPath, settings))
        {
            while (reader.Read()) { }
        }

        // 再执行自定义逻辑校验
        XmlDocument doc = new XmlDocument();
        doc.Load(xmlPath);
        XmlNodeList parentNodes = doc.SelectNodes("//Parent");

        foreach (XmlNode parent in parentNodes)
        {
            string attrValue = parent.Attributes["DeterminingAttribute"]?.Value;
            XmlNode dataNode = parent.SelectSingleNode("Data");

            if (attrValue == "A")
            {
                if (dataNode.SelectSingleNode("A1") == null || dataNode.SelectSingleNode("A2") == null)
                {
                    throw new XmlSchemaValidationException($"Parent Id={parent.Attributes["Id"].Value}的DeterminingAttribute为A,但Data中缺少A1或A2元素");
                }
                if (dataNode.SelectSingleNode("B1") != null || dataNode.SelectSingleNode("B2") != null)
                {
                    throw new XmlSchemaValidationException($"Parent Id={parent.Attributes["Id"].Value}的DeterminingAttribute为A,Data中不应包含B1或B2元素");
                }
            }
            else if (attrValue == "B")
            {
                if (dataNode.SelectSingleNode("B1") == null || dataNode.SelectSingleNode("B2") == null)
                {
                    throw new XmlSchemaValidationException($"Parent Id={parent.Attributes["Id"].Value}的DeterminingAttribute为B,但Data中缺少B1或B2元素");
                }
                if (dataNode.SelectSingleNode("A1") != null || dataNode.SelectSingleNode("A2") != null)
                {
                    throw new XmlSchemaValidationException($"Parent Id={parent.Attributes["Id"].Value}的DeterminingAttribute为B,Data中不应包含A1或A2元素");
                }
            }
            else
            {
                throw new XmlSchemaValidationException($"Parent Id={parent.Attributes["Id"].Value}的DeterminingAttribute值无效,只能是A或B");
            }
        }

        Console.WriteLine("XML校验通过");
    }

    private static void ValidationEventHandler(object sender, ValidationEventArgs e)
    {
        throw new XmlSchemaValidationException("XSD基础校验失败:" + e.Message);
    }
}

说明:

  • 宽松XSD允许Data中出现A类或B类元素,先通过基础结构校验
  • 自定义代码遍历每个Parent元素,校验DeterminingAttribute值与Data子元素的匹配关系,确保属性值和元素类型一一对应

内容的提问来源于stack exchange,提问作者Nikola

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 20:54:58