Разный результат между проверкой XML в Notepad++ и C# XMLDocument.validate

Я пытаюсь обеспечить ограничение уникальности для комбинации атрибутов двух элементов, используя XSD. Ограничение корректно работает в Notepad++, но не в моем приложении C#.

Я добавил в свой XSD следующий уникальный оператор:

<xs:unique name = "uniqueEMNameParentID">
    <xs:selector xpath = ".//EM"/>
    <xs:field xpath = "@Name"/>
    <xs:field xpath = ".//Station/@ID"/>
</xs:unique>

И это пример моего XML:

<?xml version = "1.0" encoding = "utf-8"?>
<Project>
  <PLC Name = "PLC">
    <Type>
      <TypeDef ID = "1" Name = "G05" Description = "" />
    </Type>
    <SA ID = "01">
      <Station ID = "100">
        <EM ID = "01" Name = "IR01" IsRobot = "TRUE">          
        </EM>
      </Station>
    </SA>
    <SA ID = "02">
      <Station ID = "100">
        <EM ID = "02" Name = "IR01" IsRobot = "TRUE">
        </EM>
        <EM ID = "03" Name = "IR03" IsRobot = "TRUE">      
        </EM>
      </Station>
    </SA>
  </PLC>
</Project>

При использовании Notepad++ XSD выдает правильную ошибку: «IR01 — это дубликат ключа для ограничения уникальной идентификации «uniqueEMNameParentID». Однако при проверке того же XML на соответствие XSD на C# с использованием приведенного ниже кода ошибка не выдается:

 private void ValidateXML()
 {
     XmlSchemaSet schema = new XmlSchemaSet();
     schema.Add("", @"Z:\VMshare\Maarten\WindowsFormsApplication3\WindowsFormsApplication3\XML\XSDScheme3.xsd");
     projectXML.Schemas.Add(schema);

     projectXML.Validate((object sender, System.Xml.Schema.ValidationEventArgs args) =>
     {
         MessageBox.Show(args.Message);
     });
 }

Мой XML-документ не содержит никаких пространств имен, мой XSD не содержит целевого пространства имен, а для атрибута и elementformdefault установлено значение неквалифицированное. Кто-нибудь знает, что это может быть?

Обратите внимание, что мой XML содержит два элемента Station с идентификатором атрибута = 100. Оба этих элемента имеют дочерний элемент EM с именем атрибута = IR01. Это не должно быть возможным.

Полный XSD:

<xs:schema attributeFormDefault = "unqualified" elementFormDefault = "unqualified" xmlns:xs = "http://www.w3.org/2001/XMLSchema">
    <xs:element name = "Project">
        <xs:complexType>
            <xs:sequence>
                <xs:element name = "PLC">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name = "Type">
                                <xs:complexType>
                                    <xs:sequence>
                                        <xs:element name = "TypeDef">
                                            <xs:complexType>
                                                <xs:simpleContent>
                                                    <xs:extension base = "xs:string">
                                                        <xs:attribute type = "xs:byte" name = "ID"/>
                                                        <xs:attribute type = "xs:string" name = "Name"/>
                                                        <xs:attribute type = "xs:string" name = "Description"/>
                                                    </xs:extension>
                                                </xs:simpleContent>
                                            </xs:complexType>
                                        </xs:element>
                                    </xs:sequence>
                                </xs:complexType>
                            </xs:element>
                            <xs:element name = "SA" maxOccurs = "unbounded" minOccurs = "0">
                                <xs:complexType>
                                    <xs:sequence>
                                        <xs:element name = "Station" maxOccurs = "unbounded" minOccurs = "0">
                                            <xs:complexType>
                                                <xs:sequence>
                                                    <xs:element name = "EM" maxOccurs = "unbounded" minOccurs = "0">
                                                        <xs:complexType>
                                                            <xs:sequence>
                                                                <xs:element name = "Robot">
                                                                    <xs:complexType>
                                                                        <xs:sequence>
                                                                            <xs:element name = "Jobs">
                                                                                <xs:complexType>
                                                                                    <xs:sequence>
                                                                                        <xs:element name = "Job" maxOccurs = "unbounded" minOccurs = "0">
                                                                                            <xs:complexType>
                                                                                                <xs:simpleContent>
                                                                                                    <xs:extension base = "xs:string">
                                                                                                        <xs:attribute type = "xs:byte" name = "ID" use = "required"/>
                                                                                                        <xs:attribute type = "xs:string" name = "Description" use = "required"/>
                                                                                                        <xs:attribute type = "xs:string" name = "Type" use = "optional"/>
                                                                                                    </xs:extension>
                                                                                                </xs:simpleContent>
                                                                                            </xs:complexType>
                                                                                        </xs:element>
                                                                                    </xs:sequence>
                                                                                </xs:complexType>
                                                                            </xs:element>
                                                                            <xs:element name = "Areas">
                                                                                <xs:complexType>
                                                                                    <xs:sequence>
                                                                                        <xs:element name = "Area" maxOccurs = "unbounded" minOccurs = "0">
                                                                                            <xs:complexType>
                                                                                                <xs:simpleContent>
                                                                                                    <xs:extension base = "xs:string">
                                                                                                        <xs:attribute type = "xs:byte" name = "ID" use = "required"/>
                                                                                                        <xs:attribute type = "xs:string" name = "Description" use = "required"/>
                                                                                                        <xs:attribute type = "xs:string" name = "TargetEM_No" use = "optional"/>
                                                                                                    </xs:extension>
                                                                                                </xs:simpleContent>
                                                                                            </xs:complexType>
                                                                                        </xs:element>
                                                                                    </xs:sequence>
                                                                                </xs:complexType>
                                                                            </xs:element>
                                                                            <xs:element name = "Userbits">
                                                                                <xs:complexType mixed = "true">
                                                                                    <xs:sequence>
                                                                                        <xs:element name = "Userbit" maxOccurs = "unbounded" minOccurs = "0">
                                                                                            <xs:complexType mixed = "true">
                                                                                                <xs:sequence>
                                                                                                    <xs:element name = "Reference" maxOccurs = "unbounded" minOccurs = "0">
                                                                                                        <xs:complexType>
                                                                                                            <xs:simpleContent>
                                                                                                                <xs:extension base = "xs:string">
                                                                                                                    <xs:attribute type = "xs:byte" name = "Area" use = "optional"/>
                                                                                                                    <xs:attribute type = "xs:byte" name = "Job" use = "optional"/>
                                                                                                                </xs:extension>
                                                                                                            </xs:simpleContent>
                                                                                                        </xs:complexType>
                                                                                                    </xs:element>
                                                                                                </xs:sequence>
                                                                                                <xs:attribute type = "xs:byte" name = "ID" use = "required"/>
                                                                                                <xs:attribute type = "xs:string" name = "Description" use = "required"/>
                                                                                            </xs:complexType>
                                                                                        </xs:element>
                                                                                    </xs:sequence>
                                                                                </xs:complexType>
                                                                            </xs:element>
                                                                            <xs:element name = "FZones">
                                                                                <xs:complexType mixed = "true">
                                                                                    <xs:sequence>
                                                                                        <xs:element name = "FZone" minOccurs = "0">
                                                                                            <xs:complexType>
                                                                                                <xs:simpleContent>
                                                                                                    <xs:extension base = "xs:string">
                                                                                                        <xs:attribute type = "xs:byte" name = "ID" use = "optional"/>
                                                                                                        <xs:attribute type = "xs:string" name = "Function" use = "optional"/>
                                                                                                    </xs:extension>
                                                                                                </xs:simpleContent>
                                                                                            </xs:complexType>
                                                                                        </xs:element>
                                                                                    </xs:sequence>
                                                                                </xs:complexType>
                                                                            </xs:element>
                                                                            <xs:element name = "Programs">
                                                                                <xs:complexType>
                                                                                    <xs:sequence>
                                                                                        <xs:element name = "Program" maxOccurs = "unbounded" minOccurs = "0">
                                                                                            <xs:complexType>
                                                                                                <xs:simpleContent>
                                                                                                    <xs:extension base = "xs:string">
                                                                                                        <xs:attribute type = "xs:byte" name = "ID" use = "required"/>
                                                                                                        <xs:attribute type = "xs:string" name = "JobSequence" use = "required"/>
                                                                                                        <xs:attribute type = "xs:byte" name = "Home" use = "required"/>
                                                                                                        <xs:attribute type = "xs:string" name = "Sheet" use = "required"/>
                                                                                                    </xs:extension>
                                                                                                </xs:simpleContent>
                                                                                            </xs:complexType>
                                                                                        </xs:element>
                                                                                    </xs:sequence>
                                                                                </xs:complexType>
                                                                            </xs:element>
                                                                            <xs:element name = "Tools">
                                                                                <xs:complexType>
                                                                                    <xs:sequence>
                                                                                        <xs:element name = "Tool" maxOccurs = "unbounded" minOccurs = "0">
                                                                                            <xs:complexType>
                                                                                                <xs:simpleContent>
                                                                                                    <xs:extension base = "xs:string">
                                                                                                        <xs:attribute type = "xs:byte" name = "ID" use = "required"/>
                                                                                                        <xs:attribute type = "xs:string" name = "Type" use = "required"/>
                                                                                                        <xs:attribute type = "xs:string" name = "Name" use = "required"/>
                                                                                                    </xs:extension>
                                                                                                </xs:simpleContent>
                                                                                            </xs:complexType>
                                                                                        </xs:element>
                                                                                    </xs:sequence>
                                                                                </xs:complexType>
                                                                            </xs:element>
                                                                            <xs:element name = "UserNumbers">
                                                                                <xs:complexType>
                                                                                    <xs:simpleContent>
                                                                                        <xs:extension base = "xs:string">
                                                                                            <xs:attribute type = "xs:string" name = "NumberRequired" use = "required"/>
                                                                                        </xs:extension>
                                                                                    </xs:simpleContent>
                                                                                </xs:complexType>
                                                                            </xs:element>
                                                                        </xs:sequence>
                                                                        <xs:attribute type = "xs:string" name = "Name" use = "optional"/>
                                                                        <xs:attribute type = "xs:string" name = "Manufacturer" use = "optional"/>
                                                                        <xs:attribute type = "xs:byte" name = "RobotIndex" use = "optional"/>
                                                                    </xs:complexType>
                                                                </xs:element>
                                                                <xs:element name = "TypePlaceholder" minOccurs = "0">
                                                                    <xs:complexType>
                                                                        <xs:simpleContent>
                                                                            <xs:extension base = "xs:string">
                                                                                <xs:attribute type = "xs:string" name = "Name"/>
                                                                            </xs:extension>
                                                                        </xs:simpleContent>
                                                                    </xs:complexType>
                                                                </xs:element>
                                                            </xs:sequence>
                                                            <xs:attribute type = "xs:byte" name = "ID" use = "required"/>
                                                            <xs:attribute type = "xs:string" name = "Name" use = "required"/>
                                                            <xs:attribute type = "xs:string" name = "IsRobot" use = "required"/>
                                                        </xs:complexType>
                                                    </xs:element>
                                                </xs:sequence>
                                                <xs:attribute type = "xs:short" name = "ID" use = "optional"/>
                                            </xs:complexType>

                                        </xs:element>
                                    </xs:sequence>
                                    <xs:attribute type = "xs:byte" name = "ID" use = "optional"/>
                                </xs:complexType>

                            </xs:element>
                        </xs:sequence>
                        <xs:attribute type = "xs:string" name = "Name"/>
                    </xs:complexType>
                </xs:element>
                <xs:element name = "ImportDocuments">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name = "HandshakeDocument" maxOccurs = "unbounded" minOccurs = "0">
                                <xs:complexType>
                                    <xs:simpleContent>
                                        <xs:extension base = "xs:string">
                                            <xs:attribute type = "xs:string" name = "Name" use = "required"/>
                                            <xs:attribute type = "xs:string" name = "ImportDate" use = "required"/>
                                        </xs:extension>
                                    </xs:simpleContent>
                                </xs:complexType>
                            </xs:element>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
        <xs:unique name = "uniqueEMNameParentID">
            <xs:selector xpath = ".//EM"/>
            <xs:field xpath = "@Name"/>
            <xs:field xpath = ".//Station/@ID"/>
        </xs:unique>
    </xs:element>
</xs:schema>

Полный XML

<?xml version = "1.0" encoding = "utf-8"?>
<Project>
  <PLC Name = "PLC">
    <Type>
      <TypeDef ID = "1" Name = "G05" Description = "" />
    </Type>
    <SA ID = "01">
      <Station ID = "100">
        <EM ID = "01" Name = "IR01" IsRobot = "TRUE">
          <Robot Name = "AR01" Manufacturer = "FANUC" RobotIndex = "1">
            <Jobs>
              <Job ID = "1" Description = "Handling Step 1" Type = "" />
              <Job ID = "2" Description = "Handling Step 2" Type = "" />
              <Job ID = "3" Description = "Handling Step 3" Type = "" />
              <Job ID = "4" Description = "Handling Step 4" Type = "" />
              <Job ID = "5" Description = "Handling Step 5" Type = "" />
              <Job ID = "6" Description = "Handling Step 6" Type = "" />
            </Jobs>
            <Areas>
              <Area ID = "1" Description = "100TP01" TargetEM_No = "" />
              <Area ID = "2" Description = "100FR02-FX01" TargetEM_No = "" />
            </Areas>
            <Userbits>
              <Userbit ID = "1" Description = "Userbit1: Pick_Action">
                <Reference Area = "2" Job = "5" />
              </Userbit>
              <Userbit ID = "2" Description = "Userbit2: Drop_Action">
                <Reference Area = "1" Job = "1" />
                <Reference Area = "1" Job = "2" />
                <Reference Area = "1" Job = "3" />
                <Reference Area = "3" Job = "4" />
                <Reference Area = "3" Job = "6" />
              </Userbit>
              <Userbit ID = "30" Description = "xx_Mute_Sensors">
                <Reference Area = "1" Job = "1" />
                <Reference Area = "2" Job = "1" />
                <Reference Area = "2" Job = "2" />
                <Reference Area = "1" Job = "2" />
                <Reference Area = "2" Job = "3" />
                <Reference Area = "1" Job = "3" />
                <Reference Area = "3" Job = "4" />
                <Reference Area = "2" Job = "5" />
                <Reference Area = "3" Job = "6" />
              </Userbit>
              <Userbit ID = "31" Description = "xx_Type_Transfer">
                <Reference Area = "2" Job = "1" />
                <Reference Area = "1" Job = "1" />
                <Reference Area = "1" Job = "2" />
                <Reference Area = "1" Job = "3" />
                <Reference Area = "2" Job = "3" />
                <Reference Area = "3" Job = "4" />
                <Reference Area = "2" Job = "5" />
                <Reference Area = "3" Job = "6" />
              </Userbit>
            </Userbits>
            <FZones />
            <Programs>
              <Program ID = "1" JobSequence = "1;2;3;4;5;6;" Home = "1" Sheet = "FA01++100+IR01_P1" />
              <Program ID = "2" JobSequence = "2;3;4;5;6;" Home = "1" Sheet = "FA01++100+IR01_P1" />
              <Program ID = "3" JobSequence = "3;4;5;6;" Home = "1" Sheet = "FA01++100+IR01_P1" />
              <Program ID = "4" JobSequence = "4;5;6;" Home = "1" Sheet = "FA01++100+IR01_P1" />
              <Program ID = "5" JobSequence = "5;6;" Home = "1" Sheet = "FA01++100+IR01_P1" />
              <Program ID = "6" JobSequence = "6;" Home = "1" Sheet = "FA01++100+IR01_P1" />
            </Programs>
            <Tools>
              <Tool ID = "1" Type = "spotweld gun" Name = "FG01" />
              <Tool ID = "2" Type = "adhesive gun" Name = "FG02" />
            </Tools>
            <UserNumbers NumberRequired = "True" />
          </Robot>
          <TypePlaceholder Name = "" />
        </EM>
      </Station>
    </SA>
    <SA ID = "02">
      <Station ID = "100">
        <EM ID = "02" Name = "IR01" IsRobot = "TRUE">
          <Robot Name = "AR02" Manufacturer = "FANUC" RobotIndex = "2">
            <Jobs>
              <Job ID = "1" Description = "Handling Step 1" Type = "" />
              <Job ID = "2" Description = "Handling Step 2" Type = "" />
              <Job ID = "3" Description = "Handling Step 3" Type = "" />
              <Job ID = "4" Description = "Handling Step 4" Type = "" />
              <Job ID = "5" Description = "Handling Step 5" Type = "" />
              <Area/>
            </Jobs>
            <Areas>
              <Area ID = "1" Description = "100TP02" TargetEM_No = "" />
              <Area ID = "2" Description = "100FR01-FX01" TargetEM_No = "" />
              <Area ID = "3" Description = "100FX01" TargetEM_No = "" />
            </Areas>
            <Userbits>
              <Userbit ID = "1" Description = "Userbit1: Pick_Action">
                <Reference Area = "2" Job = "4" />
              </Userbit>
              <Userbit ID = "2" Description = "Userbit2: Drop_Action">
                <Reference Area = "1" Job = "1" />
                <Reference Area = "1" Job = "2" />
                <Reference Area = "2" Job = "3" />
              </Userbit>
              <Userbit ID = "30" Description = "xx_Mute_Sensors">
                <Reference Area = "2" Job = "1" />
                <Reference Area = "1" Job = "1" />
                <Reference Area = "1" Job = "2" />
                <Reference Area = "2" Job = "2" />
                <Reference Area = "2" Job = "3" />
                <Reference Area = "2" Job = "4" />
                <Reference Area = "3" Job = "5" />
              </Userbit>
              <Userbit ID = "31" Description = "xx_Type_Transfer">
                <Reference Area = "2" Job = "1" />
                <Reference Area = "1" Job = "1" />
                <Reference Area = "1" Job = "2" />
                <Reference Area = "2" Job = "2" />
                <Reference Area = "2" Job = "3" />
                <Reference Area = "2" Job = "4" />
                <Reference Area = "3" Job = "5" />
              </Userbit>
            </Userbits>
            <FZones />
            <Programs>
              <Program ID = "1" JobSequence = "1;2;3;4;5;" Home = "1" Sheet = "FA01++100+IR02_P1" />
              <Program ID = "2" JobSequence = "2;3;4;5;" Home = "1" Sheet = "FA01++100+IR02_P1" />
              <Program ID = "3" JobSequence = "3;4;5;" Home = "1" Sheet = "FA01++100+IR02_P1" />
              <Program ID = "4" JobSequence = "4;5;" Home = "1" Sheet = "FA01++100+IR02_P1" />
              <Program ID = "5" JobSequence = "5;" Home = "1" Sheet = "FA01++100+IR02_P1" />
            </Programs>
            <Tools>
              <Tool ID = "1" Type = "gripper" Name = "FG01" />
            </Tools>
            <UserNumbers NumberRequired = "True" />
          </Robot>
          <TypePlaceholder Name = "Type" />
        </EM>
        <EM ID = "03" Name = "IR03" IsRobot = "TRUE">
          <Robot Name = "AR03" Manufacturer = "FANUC" RobotIndex = "3">
            <Jobs>
              <Job ID = "1" Description = "Tackweld Step 1" Type = "" />
              <Job ID = "2" Description = "Tackweld Step 2" Type = "" />
              <Job ID = "3" Description = "Tackweld Step 3" Type = "" />
              <Job ID = "4" Description = "Tackweld Step 4" Type = "" />
              <Job ID = "5" Description = "Tackweld Step 5" Type = "" />
              <Job ID = "6" Description = "Tackweld Step 6" Type = "" />
              <Job ID = "7" Description = "Tackweld Step7" Type = "" />
            </Jobs>
            <Areas>
              <Area ID = "1" Description = "100FR01-FX01" TargetEM_No = "" />
              <Area ID = "2" Description = "100FR02-FX01" TargetEM_No = "" />
            </Areas>
            <Userbits />
            <FZones />
            <Programs>
              <Program ID = "1" JobSequence = "1;2;3;4;5;6;7;" Home = "1" Sheet = "FA01++100+IR03_P1" />
              <Program ID = "2" JobSequence = "2;3;4;5;6;7;" Home = "1" Sheet = "FA01++100+IR03_P1" />
              <Program ID = "3" JobSequence = "3;4;5;6;7;" Home = "1" Sheet = "FA01++100+IR03_P1" />
              <Program ID = "4" JobSequence = "4;5;6;7;" Home = "1" Sheet = "FA01++100+IR03_P1" />
              <Program ID = "5" JobSequence = "5;6;7;" Home = "1" Sheet = "FA01++100+IR03_P1" />
              <Program ID = "6" JobSequence = "6;7;" Home = "1" Sheet = "FA01++100+IR03_P1" />
              <Program ID = "7" JobSequence = "7;" Home = "1" Sheet = "FA01++100+IR03_P1" />
            </Programs>
            <Tools>
              <Tool ID = "1" Type = "spotweld gun" Name = "GW01" />
            </Tools>
            <UserNumbers NumberRequired = "False" />
          </Robot>
        </EM>
      </Station>
    </SA>
  </PLC>
  <ImportDocuments>
    <HandshakeDocument Name = "DO30011_FA01_Robot_PLC_Handshake_v6" ImportDate = "18/04/2024" />
  </ImportDocuments>
</Project>

Предоставьте исполняемый файл: полную схему и исходный документ, демонстрирующий проблему. (Например, очень важно, где в вашей схеме появляется ограничение xs:unique). Также: обнаруживает ли ваш код C# другие ошибки проверки или проблемы возникают только из-за ограничения уникальности?

Michael Kay 30.04.2024 11:07

В качестве примера я добавил полный XML и XSD. Это то, что вы имели ввиду? Ограничение уникальности — единственное ограничение, создающее проблему.

Maarten Schuurmans 30.04.2024 11:20
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
84
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не знаю, почему существует разница между процессорами схемы, но я отмечаю, что в вашем ограничении уникальности поле <xs:field xpath = ".//Station/@ID"/> никогда не будет выбирать что-либо при запуске из элемента EM, поэтому оно, вероятно, не делает то, что вы намеревались.

Я считаю, что правило XSD 1.0 заключается в том, что когда одно из полей ничего не выбирает, ограничение уникальности считается выполненным. Однако это та область, в которой процессоры схемы не всегда совместимы на 100%.

Обновлять

Теперь вы предоставили больше информации. В частности, вы дали (неформальное) заявление об ограничении, которое вы пытаетесь обеспечить:

Обратите внимание, что мой XML содержит два элемента Station с идентификатором атрибута = 100. Оба этих элемента имеют дочерний элемент EM с именем атрибута = ИР01. Это не должно быть возможным.

Я не уверен, можно ли это выразить в XSD 1.0. Кажется, вы говорите, что иметь две станции с одинаковым идентификатором - это нормально, при условии, что они имеют непересекающиеся наборы значений для EM/@name.

Проблема в том, что если вы попытаетесь выразить это как ограничение на элементы Station, это потребует выбора неограниченного числа полей, но если вы попытаетесь выразить это как ограничение на элементы EM, это потребует доступа к ../@ID как к одному из поля, участвующие в ограничении, и выбор вверх не допускается.

Но, возможно, я неправильно понял ограничение.

Каков был бы правильный способ сделать это тогда? Должен ли я использовать селектор для перехода к элементу станции. Затем используйте поле xs:field, чтобы найти каждый элемент EM? Потому что, когда я пробую это сделать, он проверяет уникальность только внутри одного и того же элемента станции, а не для всех элементов станции.

Maarten Schuurmans 30.04.2024 11:31

Вы правильно поняли ограничение. Возможно иметь два элемента станции с одинаковым идентификатором. Итак, если я правильно вас понял, это ограничение не представляется возможным?

Maarten Schuurmans 30.04.2024 14:01
Ответ принят как подходящий

Вы можете выразить это с помощью Schematron и XSLT/XPath 2 или более поздней версии, я думаю, например.

<schema xmlns = "http://purl.oclc.org/dsdl/schematron" queryBinding = "xslt3">
    <pattern>
        <rule context = "PLC">
            <assert 
              test = "every $station1 in .//Station satisfies
                    every $station2 in .//Station[not($station1 is .) and $station1/@ID = @ID] satisfies 
                    not($station1/EM/@Name = $station2/EM/@Name)">duplicate Names</assert>
        </rule>
    </pattern>
</schema>

Или вы можете использовать XSD 1.1 с утверждениями, например. со схемой типа

<?xml version = "1.0" encoding = "UTF-8"?>
<xs:schema xmlns:xs = "http://www.w3.org/2001/XMLSchema"
    xmlns:vc = "http://www.w3.org/2007/XMLSchema-versioning" elementFormDefault = "qualified"
    vc:minVersion = "1.1">
    
    <xs:element name = "Project">
        <xs:complexType>
            <xs:sequence>
                <xs:element name = "PLC" maxOccurs = "unbounded">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name = "Type">
                                <xs:complexType>
                                    <xs:sequence>
                                        <xs:element name = "TypeDef">
                                            <xs:complexType>
                                                <xs:attribute name = "ID" type = "xs:integer"/>
                                                <xs:attribute name = "Name" type = "xs:string"/>
                                                <xs:attribute name = "Description" type = "xs:string"/>
                                            </xs:complexType>
                                        </xs:element>
                                    </xs:sequence>
                                </xs:complexType>
                            </xs:element>
                            <xs:element name = "SA" maxOccurs = "unbounded">
                                <xs:complexType>
                                    <xs:sequence>
                                        <xs:element name = "Station">
                                            <xs:complexType>
                                                <xs:sequence>
                                                    <xs:element name = "EM" maxOccurs = "unbounded">
                                                        <xs:complexType>
                                                            <xs:attribute name = "ID" type = "xs:integer"/>
                                                            <xs:attribute name = "Name" type = "xs:string"/>
                                                            <xs:attribute name = "IsRobot" type = "xs:string"/>
                                                        </xs:complexType>
                                                    </xs:element>
                                                </xs:sequence>
                                                <xs:attribute name = "ID" type = "xs:integer"/>
                                            </xs:complexType>
                                        </xs:element>                                        
                                    </xs:sequence>
                                    <xs:attribute name = "ID" type = "xs:integer"/>
                                </xs:complexType>
                            </xs:element>
                        </xs:sequence>
                        <xs:attribute name = "Name" type = "xs:string"/>
                        <xs:assert id = "no-duplicate-names" 
                            test = "every $station1 in .//Station satisfies
                                  every $station2 in .//Station[not($station1 is .) and $station1/@ID = @ID] satisfies 
                                  not($station1/EM/@Name = $station2/EM/@Name)"/>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>  
    
</xs:schema>

экземпляр типа

<?xml version = "1.0" encoding = "UTF-8"?>
<Project>
    <PLC Name = "PLC">
        <Type>
            <TypeDef ID = "1" Name = "G05" Description = "" />
        </Type>
        <SA ID = "01">
            <Station ID = "100">
                <EM ID = "01" Name = "IR01" IsRobot = "TRUE"></EM>
            </Station>
        </SA>
        <SA ID = "02">
            <Station ID = "100">
                <EM ID = "02" Name = "IR04" IsRobot = "TRUE"></EM>
                <EM ID = "03" Name = "IR03" IsRobot = "TRUE"></EM>
            </Station>
        </SA>
    </PLC>
</Project>

должен быть действительным, пока экземпляр типа

<?xml version = "1.0" encoding = "UTF-8"?>
<Project>
    <PLC Name = "PLC">
        <Type>
            <TypeDef ID = "1" Name = "G05" Description = "" />
        </Type>
        <SA ID = "01">
            <Station ID = "100">
                <EM ID = "01" Name = "IR01" IsRobot = "TRUE"></EM>
            </Station>
        </SA>
        <SA ID = "02">
            <Station ID = "100">
                <EM ID = "02" Name = "IR01" IsRobot = "TRUE"></EM>
                <EM ID = "03" Name = "IR03" IsRobot = "TRUE"></EM>
            </Station>
        </SA>
    </PLC>
</Project>

недействителен (и, например, SaxonEE сообщит вам

Validation error on line 3 column 21 of names-invalid.xml:
  FORG0001: Element PLC does not satisfy assertion every $station1 in .//Station satisfies
  every $station2 in .//Station[not($station1 is .) and $station1/@ID = @ID] satisfies
  not($station1/EM/@Name = $station2/EM/@Name)
  See https://www.w3.org/TR/xmlschema11-1/#sec-cvc-assertion clause 0
  Nodes for which the assertion fails:
  * element(Station) at PLC/SA[1]/Station[1]
  * element(Station) at PLC/SA[2]/Station[1]

)

XSD 1.1 поддерживается как минимум двумя библиотеками с открытым исходным кодом (Apache Xerces Java https://xerces.apache.org/xerces2-j/ , Python XMLSchema https://pypi.org/project/xmlschema/) и различные коммерческие библиотеки, такие как SaxonEE или AltovaXML.

Пример консольного приложения .NET 8, использующего библиотеку Python XmlSchema с пакетом PythonNet, доступен по адресу https://github.com/martin-honnen/PythonNETXmlSchemaTest1.

Спасибо за ваш ответ! У меня возникли проблемы с размещением правила xs:assert. Я получаю сообщение об ошибке: элемент не поддерживается в этом контексте. Я поместил его в комплекс элемента "ПЛК". Есть идеи, что это может быть? Я пробовал переносить его на разные сложные типы, но не могу заставить его работать.

Maarten Schuurmans 06.05.2024 09:07

@MaartenSchuurmans, я думаю, проблема в основном в том, что вы предполагаете, что утверждение работает с любым процессором схемы, например с процессором .NET от Microsoft. Но утверждения были введены в последней версии XSD 1.1 XSD, это было много лет назад, но, к сожалению, многие процессоры/валидаторы схемы XSD 1.0, такие как Microsoft, никогда не обновлялись для поддержки XSD 1.1. Таким образом, вам в основном придется переключиться на другой стек, что осложняется тем фактом, что Saxon EE для .NET является коммерческим, а другие варианты (с открытым исходным кодом) находятся на Java или Python, поэтому для использования из .NET вам понадобится IKVM или Python. мост.

Martin Honnen 06.05.2024 11:52

@MaartenSchuurmans, я добавил ссылку на образец консольного приложения .NET 8, которое использует пакет NuGet pythonnet для выполнения проверки XSD 1.1 с помощью библиотеки Python XmlSchema с открытым исходным кодом.

Martin Honnen 06.05.2024 16:00

Другие вопросы по теме