diff --git a/docs/python-porting-guide.md b/docs/python-porting-guide.md
index e0255b8a..0f84bec3 100644
--- a/docs/python-porting-guide.md
+++ b/docs/python-porting-guide.md
@@ -86,7 +86,7 @@ Switch-параметры (`-NoValidate`) → `action='store_true'`.
| `[System.Xml.XmlDocument] + PreserveWhitespace` | `lxml.etree.XMLParser(remove_blank_text=False)` |
| `$xmlDoc.SelectSingleNode(xpath, $ns)` | `root.find(xpath, namespaces=NSMAP)` |
| `$xmlDoc.SelectNodes(xpath, $ns)` | `root.findall(xpath, namespaces=NSMAP)` |
-| `XmlWriter + MemoryStream + BOM fix` | `etree.tostring(root, xml_declaration=True, encoding='UTF-8')` |
+| `XmlWriter + MemoryStream + BOM fix` | `etree.tostring(root, xml_declaration=True, encoding='UTF-8')` + declaration fix |
| `[System.Guid]::NewGuid().ToString()` | `str(uuid.uuid4())` |
| `$json \| ConvertFrom-Json` | `json.loads(text)` |
| `ConvertTo-Json -Depth 10` | `json.dumps(obj, ensure_ascii=False, indent=2)` |
@@ -145,8 +145,14 @@ root = tree.getroot()
# Сохранение с BOM
xml_bytes = etree.tostring(tree, xml_declaration=True, encoding='UTF-8')
-# Fix encoding case: lxml пишет utf-8, 1C ожидает UTF-8
-xml_bytes = xml_bytes.replace(b"encoding='UTF-8'", b'encoding="UTF-8"')
+# Fix declaration: etree пишет одинарные кавычки и uppercase encoding,
+# PS1 XmlWriter пишет двойные кавычки и lowercase encoding
+xml_bytes = xml_bytes.replace(
+ b"",
+ b'')
+# Trailing newline (PS1 XmlWriter добавляет, etree — нет)
+if not xml_bytes.endswith(b"\n"):
+ xml_bytes += b"\n"
with open(path, 'wb') as f:
f.write(b'\xef\xbb\xbf') # BOM
f.write(xml_bytes)
@@ -167,8 +173,36 @@ node = root.find('.//md:ChildObjects/md:Form', NSMAP)
d5p1:CatalogRef.XXX
```
-### encoding="UTF-8" (uppercase)
-1C ожидает `encoding="UTF-8"`. lxml по умолчанию пишет `encoding='UTF-8'` с одинарными кавычками — нужна замена на двойные.
+### XML declaration кавычки и encoding
+lxml/etree пишут `` (одинарные кавычки, uppercase). PS1 XmlWriter пишет `` (двойные, lowercase). 1C принимает оба варианта, но одинарные кавычки — нестандартны. Замена всего declaration — в секции "Сохранение XML с lxml".
+
+### etree vs XmlDocument — различия сериализации
+
+Python etree (lxml и stdlib) сериализует XML иначе, чем PS1 XmlDocument:
+
+| Аспект | PS1 XmlDocument | Python etree | Влияние |
+|--------|----------------|--------------|---------|
+| Declaration кавычки | `version="1.0"` | `version='1.0'` | Нестандартные одинарные кавычки |
+| Encoding case | `encoding="utf-8"` | `encoding='UTF-8'` | Косметическое |
+| Self-closing space | `` | `` | Косметическое, 1C принимает оба |
+| Trailing newline | Да | Нет | Расхождение при побайтовом сравнении |
+| Unused xmlns | Сохраняет | Удаляет | Файл валиден, но отличается от канона |
+| CR в text content | `\r` as-is | `
` entity | Разный формат, одинаковый смысл |
+| Пустые элементы | `\n` | `` | Косметическое |
+
+Все эти различия обрабатываются `normalizeXmlContent()` в тест-раннере (только для `--runtime python`). PS1 тесты остаются строгими.
+
+### Hashtable vs dict — порядок итерации
+
+PS1 `@{}` (Hashtable) итерирует ключи в порядке хэш-кодов. Python `dict` — в порядке вставки. Если порядок элементов влияет на вывод (присвоение индексов, генерация UUID), используйте `sorted()` в Python **и** `| Sort-Object` в PS1 для детерминизма.
+
+### Regex: (?i) inline flag в Python 3.11+
+
+PS1: `'^(?i)desc$'` — работает. Python 3.11+: `r'^(?i)desc$'` — ошибка. Inline-флаг `(?i)` должен быть в начале строки паттерна: `r'(?i)^desc$'` или `re.IGNORECASE`.
+
+### Обращение к отсутствующим свойствам
+
+PS1 молча возвращает `$null` при обращении к несуществующему свойству (`.empty` на массиве). Python падает с `AttributeError`. Добавляйте `isinstance()` проверки при портировании.
## Платформозависимые заметки