feat(meta-compile,meta-decompile): extra-свойства реквизита minValue/maxValue/extendedEdit + String(N,fixed) (v1.31/v0.21)

Компилятор хардкодил <ExtendedEdit>false</>, <MinValue nil>, <MaxValue nil> и
AllowedLength=Variable; декомпилятор эти свойства не захватывал. Реквизиты с
диапазоном (год 2000-3999, код цены 1-3), фикс-длина строки, расширенное
редактирование теряли данные при роундтрипе.

Зеркало form-compile:
  • minValue/maxValue — граница диапазона, типизировано: JSON-число →
    xsi:type="xs:decimal", строка → xs:string (тип сохранён декомпилятором из
    XML). Хелпер Emit-MinMaxValue (применён к Emit-Attribute/Dimension/Resource).
  • extendedEdit — bool (многострочное поле).
  • String(N,fixed) → <v8:AllowedLength>Fixed</v8:AllowedLength> (фикс. длина);
    String(N)/String(N,variable) → Variable (дефолт). Парсер типа ps1+py.

Ловушка: Parse-AttributeShorthand строит НОВЫЙ hashtable только с известными
ключами — сначала забыл добавить minValue/maxValue/extendedEdit → $parsed.minValue
был $null, компилятор молча писал nil, хотя декомпилятор значение отдавал.

Роундтрип 120: match 100→108, TOTAL 120→78 (−42), Attribute>ExtendedEdit/
MaxValue/MinValue/AllowedLength=0, новых diff нет. Регресс 46/46 ps1+py, ps1↔py
identical. spec §3.2/§4.2, кейс catalog-attr-props (+min/max/String(3,fixed)/extEdit).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-04 18:03:18 +03:00
parent 562f4617bc
commit 177c9bb0fa
6 changed files with 225 additions and 32 deletions
@@ -1,4 +1,4 @@
# meta-compile v1.30 — Compile 1C metadata object from JSON
# meta-compile v1.31 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -509,13 +509,14 @@ function Emit-TypeContent {
return
}
# String or String(N)
if ($typeStr -match '^String(\((\d+)\))?$') {
# String or String(N) or String(N,fixed|variable) — AllowedLength: Variable дефолт / Fixed (фикс. длина).
if ($typeStr -match '^String(\((\d+)(\s*,\s*(fixed|variable))?\))?$') {
$len = if ($Matches[2]) { $Matches[2] } else { "10" }
$al = if ($Matches[4] -and $Matches[4].ToLower() -eq 'fixed') { 'Fixed' } else { 'Variable' }
X "$indent<v8:Type>xs:string</v8:Type>"
X "$indent<v8:StringQualifiers>"
X "$indent`t<v8:Length>$len</v8:Length>"
X "$indent`t<v8:AllowedLength>Variable</v8:AllowedLength>"
X "$indent`t<v8:AllowedLength>$al</v8:AllowedLength>"
X "$indent</v8:StringQualifiers>"
return
}
@@ -829,6 +830,9 @@ function Parse-AttributeShorthand {
format = $val.format
editFormat = $val.editFormat
mask = if ($val.mask) { "$($val.mask)" } else { "" }
extendedEdit = if ($val.extendedEdit -eq $true) { $true } else { $false }
minValue = $val.minValue
maxValue = $val.maxValue
hasFillValue = ($val.PSObject -and $val.PSObject.Properties -and ($val.PSObject.Properties.Name -contains 'fillValue'))
fillValue = $val.fillValue
linkByType = $val.linkByType
@@ -1481,6 +1485,15 @@ function Emit-Characteristics {
X "$indent</Characteristics>"
}
# <MinValue>/<MaxValue> — граница диапазона реквизита. Нет ключа → nil (не задано). Значение типизировано
# (зеркало form-compile): число → xs:decimal, строка → xs:string (тип сохранён декомпилятором).
function Emit-MinMaxValue {
param([string]$indent, [string]$tag, $val)
if ($null -eq $val) { X "$indent<$tag xsi:nil=`"true`"/>"; return }
$t = if ($val -is [string]) { 'xs:string' } else { 'xs:decimal' }
X "$indent<$tag xsi:type=`"$t`">$(Esc-Xml "$val")</$tag>"
}
function Emit-Attribute {
param([string]$indent, $parsed, [string]$context)
# $context: "catalog", "document", "object", "processor", "tabular", "processor-tabular", "register"
@@ -1525,9 +1538,10 @@ function Emit-Attribute {
if ($parsed.mask) { X "$indent`t`t<Mask>$(Esc-XmlText $parsed.mask)</Mask>" } else { X "$indent`t`t<Mask/>" }
$multiLine = if ($parsed.multiLine -eq $true -or $parsed.flags -contains "multiline") { "true" } else { "false" }
X "$indent`t`t<MultiLine>$multiLine</MultiLine>"
X "$indent`t`t<ExtendedEdit>false</ExtendedEdit>"
X "$indent`t`t<MinValue xsi:nil=`"true`"/>"
X "$indent`t`t<MaxValue xsi:nil=`"true`"/>"
$extEdit = if ($parsed.extendedEdit -eq $true) { "true" } else { "false" }
X "$indent`t`t<ExtendedEdit>$extEdit</ExtendedEdit>"
Emit-MinMaxValue "$indent`t`t" "MinValue" $parsed.minValue
Emit-MinMaxValue "$indent`t`t" "MaxValue" $parsed.maxValue
# FillFromFillingValue — not for tabular/processor/chart/register-other
# (Chart*, AccumulationRegister/AccountingRegister/CalculationRegister don't support these)
@@ -1736,9 +1750,10 @@ function Emit-Dimension {
X "$indent`t`t<Mask/>"
$multiLine = if ($parsed.multiLine -eq $true -or $parsed.flags -contains "multiline") { "true" } else { "false" }
X "$indent`t`t<MultiLine>$multiLine</MultiLine>"
X "$indent`t`t<ExtendedEdit>false</ExtendedEdit>"
X "$indent`t`t<MinValue xsi:nil=`"true`"/>"
X "$indent`t`t<MaxValue xsi:nil=`"true`"/>"
$extEdit = if ($parsed.extendedEdit -eq $true) { "true" } else { "false" }
X "$indent`t`t<ExtendedEdit>$extEdit</ExtendedEdit>"
Emit-MinMaxValue "$indent`t`t" "MinValue" $parsed.minValue
Emit-MinMaxValue "$indent`t`t" "MaxValue" $parsed.maxValue
# InformationRegister dimensions have FillFromFillingValue
if ($registerType -eq "InformationRegister") {
@@ -1830,9 +1845,10 @@ function Emit-Resource {
X "$indent`t`t<Mask/>"
$multiLine = if ($parsed.multiLine -eq $true -or $parsed.flags -contains "multiline") { "true" } else { "false" }
X "$indent`t`t<MultiLine>$multiLine</MultiLine>"
X "$indent`t`t<ExtendedEdit>false</ExtendedEdit>"
X "$indent`t`t<MinValue xsi:nil=`"true`"/>"
X "$indent`t`t<MaxValue xsi:nil=`"true`"/>"
$extEdit = if ($parsed.extendedEdit -eq $true) { "true" } else { "false" }
X "$indent`t`t<ExtendedEdit>$extEdit</ExtendedEdit>"
Emit-MinMaxValue "$indent`t`t" "MinValue" $parsed.minValue
Emit-MinMaxValue "$indent`t`t" "MaxValue" $parsed.maxValue
# InformationRegister resources have FillFromFillingValue, FillValue
if ($registerType -eq "InformationRegister") {
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# meta-compile v1.30 — Compile 1C metadata object from JSON
# meta-compile v1.31 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -550,14 +550,15 @@ def emit_type_content(indent, type_str):
if type_str == 'Boolean':
X(f'{indent}<v8:Type>xs:boolean</v8:Type>')
return
# String or String(N)
m = re.match(r'^String(\((\d+)\))?$', type_str)
# String or String(N) or String(N,fixed|variable) — AllowedLength: Variable дефолт / Fixed (фикс. длина).
m = re.match(r'^String(\((\d+)(\s*,\s*(fixed|variable))?\))?$', type_str)
if m:
length = m.group(2) if m.group(2) else '10'
al = 'Fixed' if (m.group(4) and m.group(4).lower() == 'fixed') else 'Variable'
X(f'{indent}<v8:Type>xs:string</v8:Type>')
X(f'{indent}<v8:StringQualifiers>')
X(f'{indent}\t<v8:Length>{length}</v8:Length>')
X(f'{indent}\t<v8:AllowedLength>Variable</v8:AllowedLength>')
X(f'{indent}\t<v8:AllowedLength>{al}</v8:AllowedLength>')
X(f'{indent}</v8:StringQualifiers>')
return
# Number without params -> Number(10,0)
@@ -853,6 +854,9 @@ def parse_attribute_shorthand(val):
'format': val.get('format'),
'editFormat': val.get('editFormat'),
'mask': str(val['mask']) if val.get('mask') else '',
'extendedEdit': True if val.get('extendedEdit') is True else False,
'minValue': val.get('minValue'),
'maxValue': val.get('maxValue'),
'hasFillValue': ('fillValue' in val),
'fillValue': val.get('fillValue'),
'linkByType': val.get('linkByType'),
@@ -1552,6 +1556,14 @@ def emit_characteristics(indent, chars):
X(f'{indent}\t</xr:Characteristic>')
X(f'{indent}</Characteristics>')
def emit_min_max_value(indent, tag, val):
"""<MinValue>/<MaxValue> — граница диапазона. None → nil. Число → xs:decimal, строка → xs:string."""
if val is None:
X(f'{indent}<{tag} xsi:nil="true"/>')
return
t = 'xs:string' if isinstance(val, str) else 'xs:decimal'
X(f'{indent}<{tag} xsi:type="{t}">{esc_xml(str(val))}</{tag}>')
def emit_attribute(indent, parsed, context):
attr_name = parsed['name']
ctx_reserved = RESERVED_BY_CONTEXT.get(context)
@@ -1589,9 +1601,10 @@ def emit_attribute(indent, parsed, context):
X(f'{indent}\t\t<Mask/>')
multi_line = 'true' if (parsed.get('multiLine') is True or 'multiline' in parsed.get('flags', [])) else 'false'
X(f'{indent}\t\t<MultiLine>{multi_line}</MultiLine>')
X(f'{indent}\t\t<ExtendedEdit>false</ExtendedEdit>')
X(f'{indent}\t\t<MinValue xsi:nil="true"/>')
X(f'{indent}\t\t<MaxValue xsi:nil="true"/>')
ext_edit = 'true' if parsed.get('extendedEdit') is True else 'false'
X(f'{indent}\t\t<ExtendedEdit>{ext_edit}</ExtendedEdit>')
emit_min_max_value(f'{indent}\t\t', 'MinValue', parsed.get('minValue'))
emit_min_max_value(f'{indent}\t\t', 'MaxValue', parsed.get('maxValue'))
# FillFromFillingValue / FillValue — not for tabular/processor/chart/register-other
# (Chart*, AccumulationRegister/AccountingRegister/CalculationRegister don't support these)
if context not in ('tabular', 'processor', 'chart', 'register-other'):
@@ -1776,9 +1789,10 @@ def emit_dimension(indent, parsed, register_type):
X(f'{indent}\t\t<Mask/>')
multi_line = 'true' if (parsed.get('multiLine') is True or 'multiline' in parsed.get('flags', [])) else 'false'
X(f'{indent}\t\t<MultiLine>{multi_line}</MultiLine>')
X(f'{indent}\t\t<ExtendedEdit>false</ExtendedEdit>')
X(f'{indent}\t\t<MinValue xsi:nil="true"/>')
X(f'{indent}\t\t<MaxValue xsi:nil="true"/>')
ext_edit = 'true' if parsed.get('extendedEdit') is True else 'false'
X(f'{indent}\t\t<ExtendedEdit>{ext_edit}</ExtendedEdit>')
emit_min_max_value(f'{indent}\t\t', 'MinValue', parsed.get('minValue'))
emit_min_max_value(f'{indent}\t\t', 'MaxValue', parsed.get('maxValue'))
flags = parsed.get('flags', [])
if register_type == 'InformationRegister':
fill_from = 'true' if 'master' in flags else 'false'
@@ -1850,9 +1864,10 @@ def emit_resource(indent, parsed, register_type):
X(f'{indent}\t\t<Mask/>')
multi_line = 'true' if (parsed.get('multiLine') is True or 'multiline' in parsed.get('flags', [])) else 'false'
X(f'{indent}\t\t<MultiLine>{multi_line}</MultiLine>')
X(f'{indent}\t\t<ExtendedEdit>false</ExtendedEdit>')
X(f'{indent}\t\t<MinValue xsi:nil="true"/>')
X(f'{indent}\t\t<MaxValue xsi:nil="true"/>')
ext_edit = 'true' if parsed.get('extendedEdit') is True else 'false'
X(f'{indent}\t\t<ExtendedEdit>{ext_edit}</ExtendedEdit>')
emit_min_max_value(f'{indent}\t\t', 'MinValue', parsed.get('minValue'))
emit_min_max_value(f'{indent}\t\t', 'MaxValue', parsed.get('maxValue'))
if register_type == 'InformationRegister':
X(f'{indent}\t\t<FillFromFillingValue>false</FillFromFillingValue>')
X(f'{indent}\t\t<FillValue xsi:nil="true"/>')
@@ -1,4 +1,4 @@
# meta-decompile v0.20 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
# meta-decompile v0.21 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
#
# Пилот: только Catalog. Инверс meta-compile (omit-on-default: ключ эмитим только
@@ -156,9 +156,12 @@ function Get-TypeShorthand {
switch -regex ($raw) {
'(^|:)boolean$' { $parts += 'Boolean'; break }
'(^|:)string$' {
$len = '10'
if ($next -and $next.LocalName -eq 'StringQualifiers') { $l = $next.SelectSingleNode('v8:Length', $nsm); if ($l) { $len = $l.InnerText } }
$parts += "String($len)"; break
$len = '10'; $al = ''
if ($next -and $next.LocalName -eq 'StringQualifiers') {
$l = $next.SelectSingleNode('v8:Length', $nsm); if ($l) { $len = $l.InnerText }
$aln = $next.SelectSingleNode('v8:AllowedLength', $nsm); if ($aln -and $aln.InnerText -eq 'Fixed') { $al = ',fixed' }
}
$parts += "String($len$al)"; break
}
'(^|:)decimal$' {
$d = '10'; $f = '0'; $sign = ''
@@ -279,6 +282,19 @@ function Attr-ToDsl {
$v = & $en 'Mask'; if ($v) { $extra['mask'] = $v }
$v = & $en 'ChoiceHistoryOnInput'; if ($v -and $v -ne 'Auto') { $extra['choiceHistoryOnInput'] = $v }
$v = & $en 'FillChecking'; if ($v -eq 'ShowWarning') { $extra['fillChecking'] = 'ShowWarning' }
$v = & $en 'ExtendedEdit'; if ($v -eq 'true') { $extra['extendedEdit'] = $true }
# MinValue/MaxValue — граница диапазона (omit при nil). Тип сохраняем: xs:string→строка, xs:decimal→число.
foreach ($mm in @('MinValue','MaxValue')) {
$mn = $ap.SelectSingleNode("md:$mm", $nsm)
if (-not $mn) { continue }
if ($mn.GetAttribute('nil', 'http://www.w3.org/2001/XMLSchema-instance') -eq 'true') { continue }
$key = if ($mm -eq 'MinValue') { 'minValue' } else { 'maxValue' }
$xt = $mn.GetAttribute('type', 'http://www.w3.org/2001/XMLSchema-instance')
$txt = $mn.InnerText
if ($xt -match 'decimal|int|double|float') {
if ($txt -match '^-?\d+$') { $extra[$key] = [long]$txt } else { $extra[$key] = [double]$txt }
} else { $extra[$key] = $txt }
}
$fmtV = Get-MLValue ($ap.SelectSingleNode('md:Format', $nsm)); if ($null -ne $fmtV) { $extra['format'] = $fmtV }
$efmtV = Get-MLValue ($ap.SelectSingleNode('md:EditFormat', $nsm)); if ($null -ne $efmtV) { $extra['editFormat'] = $efmtV }
+11 -1
View File
@@ -59,7 +59,8 @@ JSON DSL для описания объектов метаданных конф
| DSL | XML |
|-----|-----|
| `String` или `String(100)` | `xs:string` + StringQualifiers |
| `String` или `String(100)` | `xs:string` + StringQualifiers (AllowedLength=Variable) |
| `String(100,fixed)` | `xs:string` + AllowedLength=Fixed (строка фикс. длины) |
| `Number(15,2)` | `xs:decimal` + NumberQualifiers |
| `Number(10,0,nonneg)` | `xs:decimal` + AllowedSign=Nonnegative |
| `Boolean` | `xs:boolean` |
@@ -180,6 +181,15 @@ JSON DSL для описания объектов метаданных конф
| `choiceHistoryOnInput` | ChoiceHistoryOnInput | Auto | Auto/DontUse |
| `indexing` | Indexing | DontIndex | флаги `index`/`indexAdditional` |
| `multiLine` | MultiLine | false | bool (флаг `multiline`) |
| `extendedEdit` | ExtendedEdit | false | bool (расширенное редактирование — многострочное поле) |
| `minValue` / `maxValue` | MinValue / MaxValue | nil (не задано) | граница диапазона, типизировано (см. ниже) |
**`minValue` / `maxValue`** — границы диапазона значений реквизита (`<MinValue>`/`<MaxValue>`). Без ключа →
`xsi:nil="true"` (не задано). Значение **типизировано** (зеркало form-compile): JSON-**число** → `xsi:type="xs:decimal"`,
JSON-**строка** → `xsi:type="xs:string"` (напр. год `"2000"`, код `"01"`). Тип сохраняется декомпилятором из XML.
**Фикс. длина строки:** тип `String(N,fixed)``<v8:AllowedLength>Fixed</v8:AllowedLength>` (строка ровно N символов);
`String(N)` / `String(N,variable)``Variable` (дефолт). См. §3.2.
**`fillValue` — значение заполнения реквизита** (`<FillValue>`). Пара с `fillFromFillingValue` — единый блок
«заполнения» (недоступен у реквизитов ТЧ; там оба свойства не эмитятся). Форма **пустого** значения зависит от
@@ -6,7 +6,10 @@
{ "name": "ИНН", "type": "String(12)", "fullTextSearch": "DontUse", "comment": "Налоговый номер",
"mask": "999999999999", "use": "ForFolderAndItem", "fillCheck": true },
{ "name": "Курс", "type": "Number(15,4)", "format": "ЧДЦ=4", "editFormat": { "ru": "ЧДЦ=4" },
"createOnInput": "Use", "quickChoice": false, "fillFromFillingValue": true, "dataHistory": "DontUse", "passwordMode": true }
"createOnInput": "Use", "quickChoice": false, "fillFromFillingValue": true, "dataHistory": "DontUse", "passwordMode": true },
{ "name": "ГодЗаключения", "type": "Number(4,0,nonneg)", "minValue": 2000, "maxValue": 3999 },
{ "name": "КодРегиона", "type": "String(3,fixed)", "minValue": "01", "maxValue": "99" },
{ "name": "Описание", "type": "String(500)", "extendedEdit": true }
]
},
"validatePath": "Catalogs/Контрагенты",
@@ -186,6 +186,139 @@
<DataHistory>DontUse</DataHistory>
</Properties>
</Attribute>
<Attribute uuid="UUID-014">
<Properties>
<Name>ГодЗаключения</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Год заключения</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Type>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>4</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Nonnegative</v8:AllowedSign>
</v8:NumberQualifiers>
</Type>
<PasswordMode>false</PasswordMode>
<Format/>
<EditFormat/>
<ToolTip/>
<MarkNegatives>false</MarkNegatives>
<Mask/>
<MultiLine>false</MultiLine>
<ExtendedEdit>false</ExtendedEdit>
<MinValue xsi:type="xs:decimal">2000</MinValue>
<MaxValue xsi:type="xs:decimal">3999</MaxValue>
<FillFromFillingValue>false</FillFromFillingValue>
<FillValue xsi:type="xs:decimal">0</FillValue>
<FillChecking>DontCheck</FillChecking>
<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
<ChoiceParameterLinks/>
<ChoiceParameters/>
<QuickChoice>Auto</QuickChoice>
<CreateOnInput>Auto</CreateOnInput>
<ChoiceForm/>
<LinkByType/>
<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
<Use>ForItem</Use>
<Indexing>DontIndex</Indexing>
<FullTextSearch>Use</FullTextSearch>
<DataHistory>Use</DataHistory>
</Properties>
</Attribute>
<Attribute uuid="UUID-015">
<Properties>
<Name>КодРегиона</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Код региона</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>3</v8:Length>
<v8:AllowedLength>Fixed</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
<PasswordMode>false</PasswordMode>
<Format/>
<EditFormat/>
<ToolTip/>
<MarkNegatives>false</MarkNegatives>
<Mask/>
<MultiLine>false</MultiLine>
<ExtendedEdit>false</ExtendedEdit>
<MinValue xsi:type="xs:string">01</MinValue>
<MaxValue xsi:type="xs:string">99</MaxValue>
<FillFromFillingValue>false</FillFromFillingValue>
<FillValue xsi:type="xs:string"/>
<FillChecking>DontCheck</FillChecking>
<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
<ChoiceParameterLinks/>
<ChoiceParameters/>
<QuickChoice>Auto</QuickChoice>
<CreateOnInput>Auto</CreateOnInput>
<ChoiceForm/>
<LinkByType/>
<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
<Use>ForItem</Use>
<Indexing>DontIndex</Indexing>
<FullTextSearch>Use</FullTextSearch>
<DataHistory>Use</DataHistory>
</Properties>
</Attribute>
<Attribute uuid="UUID-016">
<Properties>
<Name>Описание</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Описание</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>500</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
<PasswordMode>false</PasswordMode>
<Format/>
<EditFormat/>
<ToolTip/>
<MarkNegatives>false</MarkNegatives>
<Mask/>
<MultiLine>false</MultiLine>
<ExtendedEdit>true</ExtendedEdit>
<MinValue xsi:nil="true"/>
<MaxValue xsi:nil="true"/>
<FillFromFillingValue>false</FillFromFillingValue>
<FillValue xsi:type="xs:string"/>
<FillChecking>DontCheck</FillChecking>
<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
<ChoiceParameterLinks/>
<ChoiceParameters/>
<QuickChoice>Auto</QuickChoice>
<CreateOnInput>Auto</CreateOnInput>
<ChoiceForm/>
<LinkByType/>
<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
<Use>ForItem</Use>
<Indexing>DontIndex</Indexing>
<FullTextSearch>Use</FullTextSearch>
<DataHistory>Use</DataHistory>
</Properties>
</Attribute>
</ChildObjects>
</Catalog>
</MetaDataObject>