feat(mxl-compile,mxl-decompile): значение ячейки-поля ввода и настройки элемента управления

Ключ ячейки value несёт значение, набранное в поле ввода, ключ control —
сериализованные настройки самого элемента управления. Оба живут у ячейки,
а не в палитре формата: у двух ячеек с одинаковым оформлением значения
разные.

Тип значения выражается литералом JSON и приведения к объявленному типу
НЕ делается. Так пишет платформа: ссылочный и составной тип она хранит
строкой, а при смене типа ячейки прежнее значение не переписывает — на
корпусе 560 ячеек объявлены числом, но несут строку. Приведение здесь
означало бы переписывать данные при пересборке. Дата литералом JSON не
выражается, поэтому едет строкой и читается как дата только у ячейки,
объявленной датой.

Настройки элемента управления возим дословно: 26 370 вхождений корпуса
дают всего 163 различных блоба, разбирать структуру незачем. Переводы
строк внутри блоба приводим к LF на чтении — XML-парсеры двух портов
нормализуют их по-разному, и без этого порты давали разный JSON на одном
файле.

Стенд СЗначениями снова собирается байт в байт обоими портами: фикстура
обновлена до версии со значениями, флажком и блобом настроек.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-15 13:32:40 +03:00
co-authored by Claude Opus 5
parent 66bb285636
commit 43d2f660da
14 changed files with 951 additions and 17 deletions
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# mxl-decompile v1.21 — Decompile 1C spreadsheet to JSON
# mxl-decompile v1.22 — Decompile 1C spreadsheet to JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -187,6 +187,23 @@ def value_type_to_dsl(node):
return ' + '.join(parts)
def value_to_dsl(xsi_type, text):
"""Значение ячейки-поля ввода → литерал DSL. Тип значения выражается самим литералом:
число → числом, булево → true/false, строка и дата → строкой. Пустое значение пишем
как есть (0, false, пустая дата), а не схлопываем: так оно собирается обратно тем же
типом независимо от того, какой тип объявлен у ячейки."""
text = text or ''
if xsi_type == 'xs:boolean':
return text.strip() == 'true'
if xsi_type == 'xs:decimal':
s = text.strip()
try:
return int(s) if '.' not in s else float(s)
except ValueError:
return s
return text
def int_of(node, default=0):
if node is not None and node.text:
return int(node.text)
@@ -556,6 +573,22 @@ def main():
if d_node is not None and d_node.text:
detail = d_node.text
# Значение ячейки-поля ввода. Тип значения свой, из объявленного не выводится,
# поэтому читаем и его: пустое значение сворачивается в "", остальные едут как есть.
value = None
v_node = find(c_content, "d:v")
if v_node is not None:
value = (v_node.get(f'{{{XSI_NS}}}type') or '', (v_node.text or ''))
# Настройки элемента управления — сериализованный base64 у самой ячейки.
# Структуру не разбираем, возим дословно.
control = None
ctl_node = find(c_content, "d:control")
if ctl_node is not None and ctl_node.text:
# Переводы строк внутри блоба приводим к LF: XML-парсеры нормализуют их
# по-разному, и без этого порты давали разный JSON на одном файле.
control = ctl_node.text.replace('\r\n', '\n')
# Текст ячейки платформа хранит по элементу на язык. Раньше брался ПЕРВЫЙ, и всё
# остальное терялось — в корпусе ERP 98% макетов держат текст и под ru, и под en.
# Здесь собираем «язык → текст» как есть; свернётся в строку позже, когда станет
@@ -587,6 +620,8 @@ def main():
"FormatIdx": cell_fmt_idx,
"Param": param,
"Detail": detail,
"Value": value,
"Control": control,
"Text": text,
"HasText": has_text,
})
@@ -1128,6 +1163,10 @@ def main():
# форматах корпуса.
if name != "input":
dsl_cell["controlType"] = name
if cell["Value"] is not None:
dsl_cell["value"] = value_to_dsl(*cell["Value"])
if cell["Control"]:
dsl_cell["control"] = cell["Control"]
# Content
fill_type = cell_fmt["FillType"] if cell_fmt else ""