mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-09-20 00:45:54 +03:00
fix(xdto-compile,xdto-validate): не терять конструкции молча; вычитать инструкции
xdto-compile терял свойства без единого слова: на реалистичной чужой схеме из шести объявленных доезжало одно. Вложенные xs:sequence/xs:choice теперь уплощаются (модель хранит плоский список), xs:all трактуется как последовательность, xs:group и xs:attributeGroup раскрываются по ссылке — и о каждом приближении навык пишет предупреждение. Молчаливая потеря — тот же класс дефекта, что мы ловим у платформы, лечится так же: сообщением, не отказом. xdto-validate получил проверки на грабли, найденные при разработке: порядок элементов верхнего уровня (платформа отвергает пакет, не называя причины), конфликты объявлений (name+ref, type+вложенный тип, тип без разновидности), несовпадение рода базового типа, дубли имён свойств. Новые правила прогнаны по всем 760 пакетам выгрузок: всё, что породила платформа, валидно по определению, поэтому каждая ошибка там — ошибка правила. Первый прогон дал 7, и все три класса оказались реальным поведением платформы: length вместе с minLength/maxLength встречается, два пакета делят один targetNamespace (Envelope и SOAP_Envelope_1_1 в БП), form="Text" называется не только __content. Правила понижены до предупреждений либо сняты. Заодно убран шум: предупреждение о неиспользуемом import срабатывало на четверти корпуса — теперь только вместе с anyType, где оно и означает проблему. Итог: 0 ошибок на корпусе, предупреждений 53 вместо 242. Инструкции переписаны под читателя-исполнителя: убраны детали реализации и наши мерки, каталог проверок валидатора (его вывод самодостаточен), локальные пути в примерах заменены нейтральными. Таблица соответствий XSD и справочник аннотаций вынесены в xdto-compile/xsd-reference.md. Round-trip 760/760 сохранён, паритет PS/PY сохранён. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d05aef54b4
commit
7ca6dfa6b2
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: xdto-compile
|
||||
description: Создание пакета XDTO 1С из XML-схемы (XSD). Используй когда нужно добавить пакет XDTO в конфигурацию — для обмена, интеграции, веб-сервиса, разбора внешнего XML-формата
|
||||
argument-hint: -XsdPath <файл.xsd> -OutputDir <каталог-конфигурации> [-Name <имя>] [-Synonym <синоним>] [-Comment <текст>] [-Force]
|
||||
argument-hint: -XsdPath <файл.xsd> -OutputDir <каталог-исходников> [-Name <имя>] [-Synonym <синоним>] [-Comment <текст>] [-Force]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
@@ -10,12 +10,11 @@ allowed-tools:
|
||||
|
||||
# /xdto-compile — Создание пакета XDTO из XML-схемы
|
||||
|
||||
Собирает пакет XDTO по обычной XML-схеме: `XDTOPackages/<Имя>.xml`,
|
||||
Собирает пакет XDTO по XML-схеме: `XDTOPackages/<Имя>.xml`,
|
||||
`XDTOPackages/<Имя>/Ext/Package.bin` и регистрацию в `Configuration.xml`.
|
||||
|
||||
Отдельного DSL нет — входной формат это XSD. Вся механика перевода (локальные объявления
|
||||
префиксов `dNpM` на каждой ссылке, инвертированная кратность `lowerBound`/`upperBound`,
|
||||
фасеты атрибутами вместо дочерних элементов) делается навыком.
|
||||
Вход — обычная XSD. Отдельного формата описания нет: пиши схему так, как её пишут
|
||||
везде, остальное навык сделает сам.
|
||||
|
||||
## Параметры
|
||||
|
||||
@@ -23,34 +22,65 @@ allowed-tools:
|
||||
|----------|----------|
|
||||
| `XsdPath` | Путь к файлу XML-схемы |
|
||||
| `Xsd` | Схема строкой, вместо `-XsdPath` |
|
||||
| `OutputDir` | Каталог конфигурации (где лежит `Configuration.xml`) |
|
||||
| `Name` | Имя объекта метаданных. По умолчанию — из `xs:appinfo`, иначе имя файла XSD, санированное под идентификатор 1С |
|
||||
| `Synonym` | Синоним: строка (русский) или хеш-таблица `@{ru='…'; en='…'}` |
|
||||
| `OutputDir` | Каталог исходников конфигурации или расширения — там, где лежит `Configuration.xml` |
|
||||
| `Name` | Имя объекта метаданных. По умолчанию — из `xs:appinfo`, иначе имя файла XSD |
|
||||
| `Synonym` | Синоним: строка или хеш-таблица `@{ru='…'; en='…'}` |
|
||||
| `Comment` | Комментарий |
|
||||
| `Force` | Перезаписать существующий пакет |
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/xdto-compile.ps1" -XsdPath "<схема.xsd>" -OutputDir "<каталог>"
|
||||
powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/xdto-compile.ps1" -XsdPath "<схема.xsd>" -OutputDir "<каталог-исходников>"
|
||||
```
|
||||
|
||||
Примеры:
|
||||
```powershell
|
||||
... -XsdPath bank.xsd -OutputDir C:\cfsrc\erp -Name ОбменСБанком -Synonym "Обмен с банком"
|
||||
... -XsdPath fss.xsd -OutputDir C:\cfsrc\erp -Force
|
||||
... -XsdPath bank.xsd -OutputDir src -Name ОбменСБанком -Synonym "Обмен с банком"
|
||||
... -XsdPath fss.xsd -OutputDir src -Force
|
||||
```
|
||||
|
||||
## Формат входа
|
||||
## Читай предупреждения
|
||||
|
||||
Обычная XML-схема. Две вещи, которые XSD выразить не может (`nillable` у атрибута,
|
||||
`qualified` у отдельного свойства), пишутся атрибутами из пространства имён модели XDTO:
|
||||
XSD выразительнее модели XDTO. Всё, что не переносится один в один, навык переносит
|
||||
приближённо и **пишет об этом**:
|
||||
|
||||
```
|
||||
Предупреждения (2) — конструкции XSD без точного соответствия в модели XDTO:
|
||||
! Документ : вложенная xs:choice уплощена в последовательность — выбор одного из вариантов не сохранён
|
||||
! Документ : кратность на вложенной частице (<xs:sequence minOccurs/maxOccurs>) не выражается в модели XDTO
|
||||
```
|
||||
|
||||
Такое сообщение означает, что пакет собран, но схема упрощена. Если упрощение
|
||||
недопустимо — меняй схему (например, разноси варианты `xs:choice` по разным типам),
|
||||
а не игнорируй.
|
||||
|
||||
Что переносится приближённо: вложенные `xs:sequence`/`xs:choice` (уплощаются в плоский
|
||||
список свойств), `xs:all` (становится последовательностью), кратность на частице,
|
||||
`substitutionGroup`, `xs:key`/`keyref`/`unique`, `xs:redefine`.
|
||||
|
||||
`xs:group` и `xs:attributeGroup` раскрываются по ссылке — их содержимое попадает в тип.
|
||||
`xs:include` игнорируется: зависимости в XDTO разрешаются только по namespace,
|
||||
поэтому включаемую схему нужно собрать отдельным пакетом и заменить `include` на `import`.
|
||||
|
||||
## Зависимости между пакетами
|
||||
|
||||
`<xs:import namespace="…"/>` разрешается по namespace среди пакетов конфигурации
|
||||
или расширения.
|
||||
Если пакета с таким пространством имён нет, платформа при загрузке молча подменит тип
|
||||
на `xs:anyType` — без ошибки. Собирай сначала зависимости, потом зависящий пакет,
|
||||
и проверяй результат через `/xdto-validate`.
|
||||
|
||||
## Что XSD выразить не может
|
||||
|
||||
Две вещи модель XDTO умеет, а XML Schema — нет: `nillable` у атрибута и `qualified`
|
||||
у отдельного свойства. Они пишутся атрибутами из пространства имён модели:
|
||||
|
||||
```xml
|
||||
<xs:attribute name="Представление" type="xs:string"
|
||||
xmlns:xdto="http://v8.1c.ru/8.1/xdto" xdto:nillable="true"/>
|
||||
```
|
||||
|
||||
Правило: **чего XSD сказать не может — пиши атрибутом `xdto:` с тем же именем, что в модели**.
|
||||
Схема при этом остаётся валидной. Полный список аннотаций — в справке ниже.
|
||||
Схема остаётся валидной — валидаторы такие атрибуты игнорируют. Полный список
|
||||
и таблица соответствий XSD ↔ XDTO — в [xsd-reference.md](xsd-reference.md).
|
||||
|
||||
Свойства объекта метаданных можно задать прямо в схеме:
|
||||
|
||||
@@ -65,29 +95,17 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/xdto-compile.ps1" -
|
||||
</xs:annotation>
|
||||
```
|
||||
|
||||
Принимается и не вполне каноническая схема: имя типа без префикса трактуется как тип
|
||||
целевого пространства имён, `form="qualified"` на глобальных объявлениях (так экспортирует
|
||||
сам Конфигуратор) — читается.
|
||||
|
||||
## Зависимости между пакетами
|
||||
|
||||
`<xs:import namespace="…"/>` разрешается по namespace среди пакетов конфигурации.
|
||||
Если пакета с таким пространством имён в конфигурации нет, платформа при загрузке молча
|
||||
подменит тип на `xs:anyType` — без ошибки. Сначала соберите пакеты-зависимости,
|
||||
затем зависящий, и проверьте результат через `/xdto-validate`.
|
||||
|
||||
## Типичный workflow
|
||||
|
||||
1. Получить XSD от контрагента (или выгрузить: Конфигуратор → пакет → Экспорт XML-схемы)
|
||||
2. `/xdto-compile -XsdPath <файл> -OutputDir <конфигурация>`
|
||||
3. `/xdto-validate` — убедиться, что все типы разрешились
|
||||
4. `/db-load-xml` + `/db-update` — загрузить в базу
|
||||
1. Получить XSD от контрагента (или выгрузить схему существующего пакета: `/xdto-decompile`)
|
||||
2. `/xdto-compile -XsdPath <файл> -OutputDir <каталог-исходников>` — прочитать предупреждения
|
||||
3. `/xdto-validate` — убедиться, что типы разрешились
|
||||
4. `/db-load-xml` + `/db-update`
|
||||
|
||||
Правка существующего пакета: `/xdto-decompile` → правка XSD → `/xdto-compile -Force`.
|
||||
Пара замыкается без потерь, включая свойства объекта метаданных.
|
||||
|
||||
## Верификация
|
||||
|
||||
```
|
||||
/xdto-validate <каталог>/XDTOPackages/<Имя> — типы, импорты, регистрация
|
||||
/xdto-validate <каталог-исходников>/XDTOPackages/<Имя>
|
||||
```
|
||||
|
||||
@@ -265,6 +265,13 @@ function Serialize-Node($node, [int]$depth, $inherited) {
|
||||
|
||||
# --- XSD reading helpers ---
|
||||
|
||||
# Предупреждения о том, что XSD выражает, а модель XDTO — нет. Молча ронять
|
||||
# такие конструкции нельзя: пакет соберётся, а половина свойств исчезнет.
|
||||
$script:warnings = New-Object System.Collections.ArrayList
|
||||
function Warn([string]$msg) {
|
||||
if (-not $script:warnings.Contains($msg)) { [void]$script:warnings.Add($msg) }
|
||||
}
|
||||
|
||||
function XA([System.Xml.XmlElement]$el, [string]$name) {
|
||||
if ($el.HasAttribute($name)) { return $el.GetAttribute($name) }
|
||||
return $null
|
||||
@@ -471,6 +478,63 @@ function Build-Property([System.Xml.XmlElement]$el, [bool]$isAttribute) {
|
||||
|
||||
# --- complexType -> objectType / typeDef(ObjectType) ---
|
||||
|
||||
# Разрешение xs:group / xs:attributeGroup по ссылке
|
||||
$script:GROUPS = @{}
|
||||
$script:ATTR_GROUPS = @{}
|
||||
function Resolve-Group([System.Xml.XmlElement]$el, [string]$kind) {
|
||||
$ref = XA $el "ref"
|
||||
if (-not $ref) { return $null }
|
||||
$q = Split-QName $el $ref
|
||||
if (-not $q) { return $null }
|
||||
$map = if ($kind -eq "group") { $script:GROUPS } else { $script:ATTR_GROUPS }
|
||||
if ($map.ContainsKey($q.Local)) { return $map[$q.Local] }
|
||||
return $null
|
||||
}
|
||||
|
||||
# Модель XDTO знает только плоский список свойств: вложенные частицы уплощаются.
|
||||
# Каждое уплощение — предупреждение, потому что меняется смысл схемы.
|
||||
function Collect-Particle([System.Xml.XmlElement]$particle, $elemList, [ref]$isOpen, [string]$typeName, [int]$depth) {
|
||||
if ($depth -gt 20) { return }
|
||||
foreach ($c in $particle.ChildNodes) {
|
||||
if ($c.NodeType -ne [System.Xml.XmlNodeType]::Element -or $c.NamespaceURI -ne $XS_NS) { continue }
|
||||
switch ($c.get_LocalName()) {
|
||||
"element" { [void]$elemList.Add((Build-Property $c $false)) }
|
||||
"any" { $isOpen.Value = $true }
|
||||
"sequence" {
|
||||
Warn "$typeName : вложенная xs:sequence уплощена — модель XDTO хранит плоский список свойств"
|
||||
Collect-Particle $c $elemList $isOpen $typeName ($depth + 1)
|
||||
}
|
||||
"choice" {
|
||||
Warn "$typeName : вложенная xs:choice уплощена в последовательность — выбор одного из вариантов не сохранён"
|
||||
Collect-Particle $c $elemList $isOpen $typeName ($depth + 1)
|
||||
}
|
||||
"all" {
|
||||
Warn "$typeName : xs:all трактуется как последовательность"
|
||||
Collect-Particle $c $elemList $isOpen $typeName ($depth + 1)
|
||||
}
|
||||
"group" {
|
||||
$g = Resolve-Group $c "group"
|
||||
if ($g) {
|
||||
foreach ($gc in $g.ChildNodes) {
|
||||
if ($gc.NodeType -eq [System.Xml.XmlNodeType]::Element -and $gc.NamespaceURI -eq $XS_NS -and
|
||||
@("sequence", "choice", "all") -contains $gc.get_LocalName()) {
|
||||
Collect-Particle $gc $elemList $isOpen $typeName ($depth + 1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Warn "$typeName : не найдена группа $(XA $c 'ref') — её свойства в пакет не попали"
|
||||
}
|
||||
}
|
||||
}
|
||||
# Кратность на самой частице модель выразить не может
|
||||
if (@("sequence", "choice", "all", "group") -contains $c.get_LocalName()) {
|
||||
if ((XA $c "maxOccurs") -or (XA $c "minOccurs")) {
|
||||
Warn "$typeName : кратность на вложенной частице (<xs:$($c.get_LocalName()) minOccurs/maxOccurs>) не выражается в модели XDTO"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# open / ordered / sequenced / abstract / mixed: выводим где выводимо,
|
||||
# остальное приходит зеркалом xdto:
|
||||
function Set-TypeFlags($node, [System.Xml.XmlElement]$ct, $isOpen, $choice) {
|
||||
@@ -532,22 +596,48 @@ function Fill-ComplexType($node, [System.Xml.XmlElement]$ct) {
|
||||
# Particle: xs:sequence (ordered) or xs:choice (ordered="false")
|
||||
$seq = XFirst $body "sequence"
|
||||
$cho = XFirst $body "choice"
|
||||
$particle = if ($seq) { $seq } else { $cho }
|
||||
$all = XFirst $body "all"
|
||||
$grp = XFirst $body "group"
|
||||
$particle = if ($seq) { $seq } elseif ($cho) { $cho } elseif ($all) { $all } else { $grp }
|
||||
$isOpen = $false
|
||||
|
||||
# Порядок в XDTO: сначала form="Attribute", потом остальные (верно для 96.5%
|
||||
# типов корпуса). Отклонения приходят зеркалом xdto:order.
|
||||
$elemProps = New-Object System.Collections.ArrayList
|
||||
$typeName = if ($ct.HasAttribute("name")) { $ct.GetAttribute("name") } else { "(анонимный тип)" }
|
||||
if ($particle) {
|
||||
foreach ($c in $particle.ChildNodes) {
|
||||
if ($c.NodeType -ne [System.Xml.XmlNodeType]::Element -or $c.NamespaceURI -ne $XS_NS) { continue }
|
||||
switch ($c.get_LocalName()) {
|
||||
"element" { [void]$elemProps.Add((Build-Property $c $false)) }
|
||||
"any" { $isOpen = $true }
|
||||
}
|
||||
$openRef = [ref]$isOpen
|
||||
if ($all) {
|
||||
Warn "$typeName : xs:all трактуется как последовательность"
|
||||
}
|
||||
if ($grp -and -not $seq -and -not $cho -and -not $all) {
|
||||
# Корневая частица задана ссылкой на группу — раскрываем её содержимое
|
||||
$g = Resolve-Group $grp "group"
|
||||
if ($g) {
|
||||
foreach ($gc in $g.ChildNodes) {
|
||||
if ($gc.NodeType -eq [System.Xml.XmlNodeType]::Element -and $gc.NamespaceURI -eq $XS_NS -and
|
||||
@("sequence", "choice", "all") -contains $gc.get_LocalName()) {
|
||||
Collect-Particle $gc $elemProps $openRef $typeName 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Warn "$typeName : не найдена группа $(XA $grp 'ref') — её свойства в пакет не попали"
|
||||
}
|
||||
} else {
|
||||
Collect-Particle $particle $elemProps $openRef $typeName 0
|
||||
}
|
||||
$isOpen = $openRef.Value
|
||||
}
|
||||
foreach ($a in (XChildren $body "attribute")) { Add-Child $node (Build-Property $a $true) }
|
||||
# xs:attributeGroup раскрываем по ссылке
|
||||
foreach ($ag in (XChildren $body "attributeGroup")) {
|
||||
$g = Resolve-Group $ag "attributeGroup"
|
||||
if ($g) {
|
||||
foreach ($a in (XChildren $g "attribute")) { Add-Child $node (Build-Property $a $true) }
|
||||
} else {
|
||||
Warn "Не найдена группа атрибутов $(XA $ag 'ref') — её атрибуты в пакет не попали"
|
||||
}
|
||||
}
|
||||
foreach ($e in $elemProps) { Add-Child $node $e }
|
||||
if ((XChildren $body "anyAttribute").Count -gt 0) { $isOpen = $true }
|
||||
|
||||
@@ -591,10 +681,37 @@ if ($ann) {
|
||||
}
|
||||
}
|
||||
|
||||
# Реестр глобальных групп — нужен до обхода, чтобы раскрывать ссылки
|
||||
foreach ($node in $schema.ChildNodes) {
|
||||
if ($node.NodeType -ne [System.Xml.XmlNodeType]::Element -or $node.NamespaceURI -ne $XS_NS) { continue }
|
||||
$nm = XA $node "name"
|
||||
if ($node.get_LocalName() -eq "group" -and $nm) { $script:GROUPS[$nm] = $node }
|
||||
if ($node.get_LocalName() -eq "attributeGroup" -and $nm) { $script:ATTR_GROUPS[$nm] = $node }
|
||||
}
|
||||
|
||||
# Конструкции XSD, которым в модели XDTO нет соответствия
|
||||
foreach ($sg in $schema.SelectNodes("//*[local-name()='element'][@substitutionGroup]")) {
|
||||
Warn "Подстановочные группы (substitutionGroup) не поддерживаются моделью XDTO — объявление $($sg.GetAttribute('name')) сохранено как обычное"
|
||||
}
|
||||
foreach ($idc in @("key", "keyref", "unique")) {
|
||||
if ($schema.SelectNodes("//*[local-name()='$idc']").Count -gt 0) {
|
||||
Warn "Ограничения целостности (xs:$idc) в модели XDTO не хранятся — отброшены"
|
||||
}
|
||||
}
|
||||
if ($schema.SelectNodes("//*[local-name()='redefine']").Count -gt 0) {
|
||||
Warn "xs:redefine не поддерживается — переопределения проигнорированы"
|
||||
}
|
||||
if ($schema.SelectNodes("//*[local-name()='include']").Count -gt 0) {
|
||||
Warn "xs:include проигнорирован: модель XDTO разрешает зависимости только по namespace. Соберите включаемую схему отдельным пакетом и добавьте <xs:import>"
|
||||
}
|
||||
|
||||
foreach ($node in $schema.ChildNodes) {
|
||||
if ($node.NodeType -ne [System.Xml.XmlNodeType]::Element -or $node.NamespaceURI -ne $XS_NS) { continue }
|
||||
switch ($node.get_LocalName()) {
|
||||
"annotation" { }
|
||||
"group" { }
|
||||
"attributeGroup" { }
|
||||
"notation" { }
|
||||
"import" {
|
||||
$n = New-Node "import"
|
||||
Add-Attr $n "namespace" (XA $node "namespace")
|
||||
@@ -765,6 +882,12 @@ Write-Host "✓ Пакет XDTO собран: $Name"
|
||||
Write-Host " Namespace: $targetNs"
|
||||
Write-Host " Типов: $typeCount"
|
||||
Write-Host " Файлы: XDTOPackages/$Name.xml, XDTOPackages/$Name/Ext/Package.bin"
|
||||
if ($script:warnings.Count -gt 0) {
|
||||
Write-Host ""
|
||||
Write-Host "Предупреждения ($($script:warnings.Count)) — конструкции XSD без точного соответствия в модели XDTO:"
|
||||
foreach ($w in $script:warnings) { Write-Host " ! $w" }
|
||||
Write-Host ""
|
||||
}
|
||||
switch ($regResult) {
|
||||
"added" { Write-Host " Configuration.xml: <XDTOPackage>$Name</XDTOPackage> добавлен в ChildObjects" }
|
||||
"already" { Write-Host " Configuration.xml: <XDTOPackage>$Name</XDTOPackage> уже зарегистрирован" }
|
||||
|
||||
@@ -297,6 +297,20 @@ def serialize_node(node, depth, inherited):
|
||||
|
||||
# ── XSD reading helpers ──────────────────────────────────────
|
||||
|
||||
# Предупреждения о том, что XSD выражает, а модель XDTO — нет. Молча ронять
|
||||
# такие конструкции нельзя: пакет соберётся, а половина свойств исчезнет.
|
||||
warnings_list = []
|
||||
|
||||
|
||||
def warn(msg):
|
||||
if msg not in warnings_list:
|
||||
warnings_list.append(msg)
|
||||
|
||||
|
||||
GROUPS = {}
|
||||
ATTR_GROUPS = {}
|
||||
|
||||
|
||||
def MA(el, name):
|
||||
# xdto: mirror attribute — литеральное значение для Package.bin.
|
||||
# Ищем по namespace, а не по строке префикса.
|
||||
@@ -497,6 +511,54 @@ def build_property(el, is_attribute):
|
||||
|
||||
# ── complexType -> objectType / typeDef(ObjectType) ──────────
|
||||
|
||||
def resolve_group(el, kind):
|
||||
ref = el.get("ref")
|
||||
if not ref:
|
||||
return None
|
||||
q = split_qname(el, ref)
|
||||
if not q:
|
||||
return None
|
||||
m = GROUPS if kind == "group" else ATTR_GROUPS
|
||||
return m.get(q[1])
|
||||
|
||||
|
||||
# Модель XDTO знает только плоский список свойств: вложенные частицы уплощаются.
|
||||
# Каждое уплощение — предупреждение, потому что меняется смысл схемы.
|
||||
def collect_particle(particle, elem_list, open_flag, type_name, depth):
|
||||
if depth > 20:
|
||||
return
|
||||
for c in particle:
|
||||
if not isinstance(c.tag, str) or etree.QName(c).namespace != XS_NS:
|
||||
continue
|
||||
ln = local(c)
|
||||
if ln == "element":
|
||||
elem_list.append(build_property(c, False))
|
||||
elif ln == "any":
|
||||
open_flag[0] = True
|
||||
elif ln == "sequence":
|
||||
warn(type_name + " : вложенная xs:sequence уплощена — модель XDTO хранит плоский список свойств")
|
||||
collect_particle(c, elem_list, open_flag, type_name, depth + 1)
|
||||
elif ln == "choice":
|
||||
warn(type_name + " : вложенная xs:choice уплощена в последовательность — выбор одного из вариантов не сохранён")
|
||||
collect_particle(c, elem_list, open_flag, type_name, depth + 1)
|
||||
elif ln == "all":
|
||||
warn(type_name + " : xs:all трактуется как последовательность")
|
||||
collect_particle(c, elem_list, open_flag, type_name, depth + 1)
|
||||
elif ln == "group":
|
||||
g = resolve_group(c, "group")
|
||||
if g is not None:
|
||||
for gc in g:
|
||||
if isinstance(gc.tag, str) and etree.QName(gc).namespace == XS_NS \
|
||||
and local(gc) in ("sequence", "choice", "all"):
|
||||
collect_particle(gc, elem_list, open_flag, type_name, depth + 1)
|
||||
else:
|
||||
warn(type_name + " : не найдена группа " + str(c.get("ref")) + " — её свойства в пакет не попали")
|
||||
if ln in ("sequence", "choice", "all", "group"):
|
||||
if c.get("maxOccurs") is not None or c.get("minOccurs") is not None:
|
||||
warn(type_name + " : кратность на вложенной частице (<xs:" + ln +
|
||||
" minOccurs/maxOccurs>) не выражается в модели XDTO")
|
||||
|
||||
|
||||
def set_type_flags(node, ct, is_open, choice):
|
||||
m_open = MA(ct, "open")
|
||||
if m_open is not None:
|
||||
@@ -562,22 +624,40 @@ def fill_complex_type(node, ct):
|
||||
|
||||
seq = xfirst(body, "sequence")
|
||||
cho = xfirst(body, "choice")
|
||||
particle = seq if seq is not None else cho
|
||||
is_open = False
|
||||
all_ = xfirst(body, "all")
|
||||
grp = xfirst(body, "group")
|
||||
particle = seq if seq is not None else (cho if cho is not None else (all_ if all_ is not None else grp))
|
||||
open_flag = [False]
|
||||
|
||||
# Порядок в XDTO: сначала form="Attribute", потом остальные (96.5% типов корпуса)
|
||||
elem_props = []
|
||||
type_name = ct.get("name") or "(анонимный тип)"
|
||||
if particle is not None:
|
||||
for c in particle:
|
||||
if not isinstance(c.tag, str) or etree.QName(c).namespace != XS_NS:
|
||||
continue
|
||||
ln = local(c)
|
||||
if ln == "element":
|
||||
elem_props.append(build_property(c, False))
|
||||
elif ln == "any":
|
||||
is_open = True
|
||||
if all_ is not None:
|
||||
warn(type_name + " : xs:all трактуется как последовательность")
|
||||
if grp is not None and seq is None and cho is None and all_ is None:
|
||||
# Корневая частица задана ссылкой на группу — раскрываем её содержимое
|
||||
g = resolve_group(grp, "group")
|
||||
if g is not None:
|
||||
for gc in g:
|
||||
if isinstance(gc.tag, str) and etree.QName(gc).namespace == XS_NS \
|
||||
and local(gc) in ("sequence", "choice", "all"):
|
||||
collect_particle(gc, elem_props, open_flag, type_name, 1)
|
||||
else:
|
||||
warn(type_name + " : не найдена группа " + str(grp.get("ref")) + " — её свойства в пакет не попали")
|
||||
else:
|
||||
collect_particle(particle, elem_props, open_flag, type_name, 0)
|
||||
is_open = open_flag[0]
|
||||
for a in xchildren(body, "attribute"):
|
||||
node.children.append(build_property(a, True))
|
||||
# xs:attributeGroup раскрываем по ссылке
|
||||
for ag in xchildren(body, "attributeGroup"):
|
||||
g = resolve_group(ag, "attributeGroup")
|
||||
if g is not None:
|
||||
for a in xchildren(g, "attribute"):
|
||||
node.children.append(build_property(a, True))
|
||||
else:
|
||||
warn("Не найдена группа атрибутов " + str(ag.get("ref")) + " — её атрибуты в пакет не попали")
|
||||
node.children.extend(elem_props)
|
||||
if xchildren(body, "anyAttribute"):
|
||||
is_open = True
|
||||
@@ -627,6 +707,30 @@ if ann is not None:
|
||||
elif ln == "synonym":
|
||||
meta_synonym.append({"Lang": f.get("lang") or "", "Content": f.text or ""})
|
||||
|
||||
# Реестр глобальных групп — нужен до обхода, чтобы раскрывать ссылки
|
||||
for node in schema:
|
||||
if not isinstance(node.tag, str) or etree.QName(node).namespace != XS_NS:
|
||||
continue
|
||||
nm = node.get("name")
|
||||
if local(node) == "group" and nm:
|
||||
GROUPS[nm] = node
|
||||
if local(node) == "attributeGroup" and nm:
|
||||
ATTR_GROUPS[nm] = node
|
||||
|
||||
# Конструкции XSD, которым в модели XDTO нет соответствия
|
||||
for sg in schema.iter():
|
||||
if isinstance(sg.tag, str) and local(sg) == "element" and sg.get("substitutionGroup"):
|
||||
warn("Подстановочные группы (substitutionGroup) не поддерживаются моделью XDTO — объявление "
|
||||
+ str(sg.get("name")) + " сохранено как обычное")
|
||||
for idc in ("key", "keyref", "unique"):
|
||||
if any(isinstance(e.tag, str) and local(e) == idc for e in schema.iter()):
|
||||
warn("Ограничения целостности (xs:" + idc + ") в модели XDTO не хранятся — отброшены")
|
||||
if any(isinstance(e.tag, str) and local(e) == "redefine" for e in schema.iter()):
|
||||
warn("xs:redefine не поддерживается — переопределения проигнорированы")
|
||||
if any(isinstance(e.tag, str) and local(e) == "include" for e in schema.iter()):
|
||||
warn("xs:include проигнорирован: модель XDTO разрешает зависимости только по namespace. "
|
||||
"Соберите включаемую схему отдельным пакетом и добавьте <xs:import>")
|
||||
|
||||
for node in schema:
|
||||
if not isinstance(node.tag, str) or etree.QName(node).namespace != XS_NS:
|
||||
continue
|
||||
@@ -635,7 +739,7 @@ for node in schema:
|
||||
n = Node("import")
|
||||
add_attr(n, "namespace", node.get("namespace"))
|
||||
pkg_node.children.append(n)
|
||||
elif ln in ("annotation", "include"):
|
||||
elif ln in ("annotation", "include", "group", "attributeGroup", "notation"):
|
||||
continue
|
||||
elif ln == "element":
|
||||
pkg_node.children.append(build_property(node, False))
|
||||
@@ -772,6 +876,13 @@ print(f"✓ Пакет XDTO собран: {name}")
|
||||
print(f" Namespace: {target_ns}")
|
||||
print(f" Типов: {type_count}")
|
||||
print(f" Файлы: XDTOPackages/{name}.xml, XDTOPackages/{name}/Ext/Package.bin")
|
||||
if warnings_list:
|
||||
print("")
|
||||
print("Предупреждения (" + str(len(warnings_list)) +
|
||||
") — конструкции XSD без точного соответствия в модели XDTO:")
|
||||
for w in warnings_list:
|
||||
print(" ! " + w)
|
||||
print("")
|
||||
if reg_result == "added":
|
||||
print(f" Configuration.xml: <XDTOPackage>{name}</XDTOPackage> добавлен в ChildObjects")
|
||||
elif reg_result == "already":
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# XSD ↔ XDTO — справочник
|
||||
|
||||
## Соответствия
|
||||
|
||||
| XML Schema | Модель XDTO |
|
||||
|---|---|
|
||||
| `xs:schema/@targetNamespace` | пространство имён пакета |
|
||||
| `elementFormDefault` / `attributeFormDefault` | `elementFormQualified` / `attributeFormQualified` |
|
||||
| `xs:import/@namespace` | зависимость от другого пакета (разрешается по namespace) |
|
||||
| `xs:complexType` | объектный тип |
|
||||
| `xs:simpleType` | тип значения |
|
||||
| `xs:element` / `xs:attribute` на верхнем уровне | глобальное свойство пакета |
|
||||
| `xs:element` / `xs:attribute` внутри типа | свойство типа |
|
||||
| `@minOccurs` / `@maxOccurs="unbounded"` | `lowerBound` / `upperBound="-1"` |
|
||||
| `@nillable`, `@default`, `@fixed`, `@ref` | те же по смыслу |
|
||||
| анонимный `xs:simpleType`/`xs:complexType` в объявлении | встроенный тип свойства |
|
||||
| `xs:complexContent/xs:extension/@base` | наследование типа |
|
||||
| `@abstract`, `@mixed` | те же |
|
||||
| `xs:choice` | тип-выбор одного из вариантов |
|
||||
| `xs:any` + `xs:anyAttribute` | открытый тип |
|
||||
| `xs:simpleContent/xs:extension/@base` | свойство собственного значения элемента |
|
||||
| `xs:restriction` + фасеты | базовый тип + ограничения |
|
||||
| `xs:pattern`, `xs:enumeration` | те же |
|
||||
| `xs:list/@itemType` | список |
|
||||
| `xs:union/@memberTypes` | объединение |
|
||||
|
||||
Порядок объявлений верхнего уровня в XSD произвольный — навык сам расставит их
|
||||
в порядке, который требует модель.
|
||||
|
||||
## Аннотации `xdto:`
|
||||
|
||||
Пространство имён — `http://v8.1c.ru/8.1/xdto`. Нужны только там, где XML Schema
|
||||
не может выразить то, что умеет модель. В большинстве схем не нужны вовсе.
|
||||
|
||||
Правило: **чего XSD сказать не может — пиши атрибутом `xdto:` с тем же именем,
|
||||
что и в модели**.
|
||||
|
||||
| Аннотация | Где | Назначение |
|
||||
|---|---|---|
|
||||
| `xdto:nillable` | `xs:attribute` | `nillable` у свойства-атрибута (XSD допускает только у элементов) |
|
||||
| `xdto:lowerBound`, `xdto:upperBound` | `xs:attribute` | кратность свойства-атрибута |
|
||||
| `xdto:qualified` | объявление | переопределение `*FormQualified` для одного свойства |
|
||||
| `xdto:name` | объявление | имя свойства, если XML-имя не годится как идентификатор 1С; XML-имя уйдёт в `localName` |
|
||||
| `xdto:form` | `xs:element` | записать `form` явно |
|
||||
| `xdto:variety` | `xs:restriction`, `xs:list`, `xs:union` | записать разновидность типа явно |
|
||||
| `xdto:open`, `xdto:abstract`, `xdto:mixed`, `xdto:ordered`, `xdto:sequenced` | `xs:complexType` | флаги типа, не выводимые из модели содержимого |
|
||||
| `xdto:order` | `xs:complexType` | исходный порядок свойств, если он не «атрибуты первыми»; имена через `\|` |
|
||||
| `xdto:textName`, `xdto:textlowerBound`, `xdto:textupperBound`, `xdto:textnillable` | `xs:extension` в `xs:simpleContent` | параметры свойства собственного значения |
|
||||
| `xdto:type` | `xs:enumeration` | тип литерала перечисления |
|
||||
| `xdto:prefix` | объявление | осмысленный префикс пространства имён вместо генерируемого |
|
||||
| `xdto:memberTypesForm="prefixed"` | `xs:union` | записать состав объединения префиксами, а не `{ns}имя` |
|
||||
| `xdto:declareNs` | `xs:union` | объявить префикс пространства имён на узле |
|
||||
| `xdto:elementFormQualified`, `xdto:attributeFormQualified` | `xs:schema` | записать флаги явно |
|
||||
|
||||
Пример:
|
||||
|
||||
```xml
|
||||
<xs:complexType name="КонтактнаяИнформация" xdto:sequenced="true">
|
||||
<xs:sequence>
|
||||
<xs:element name="Комментарий" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="Адрес по документу" type="xs:string"
|
||||
xdto:name="Адрес_по_документу" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Представление" type="xs:string"
|
||||
xdto:nillable="true" xdto:lowerBound="0"/>
|
||||
</xs:complexType>
|
||||
```
|
||||
|
||||
Аннотации, которые проставляет `/xdto-decompile` при выгрузке существующего пакета,
|
||||
писать вручную не нужно — они нужны, чтобы обратная сборка вернула ровно тот же файл.
|
||||
|
||||
## Свойства объекта метаданных
|
||||
|
||||
```xml
|
||||
<xs:annotation>
|
||||
<xs:appinfo>
|
||||
<xdto:package xmlns:xdto="http://v8.1c.ru/8.1/xdto">
|
||||
<xdto:name>ОбменСБанком</xdto:name>
|
||||
<xdto:synonym lang="ru">Обмен с банком</xdto:synonym>
|
||||
<xdto:synonym lang="en">Bank exchange</xdto:synonym>
|
||||
<xdto:comment>Формат 1С:Предприятие — Клиент банка</xdto:comment>
|
||||
</xdto:package>
|
||||
</xs:appinfo>
|
||||
</xs:annotation>
|
||||
```
|
||||
|
||||
Пространство имён пакета берётся из `targetNamespace` и здесь не дублируется.
|
||||
Параметры `-Name`, `-Synonym`, `-Comment` имеют приоритет над этим блоком.
|
||||
Reference in New Issue
Block a user