Что плохого в построении xml с конкатенацией строк?

Введение в XML¶

XML ( англ. eXtensible Markup Language) — расширяемый язык разметки,
предназначенный для хранения и передачи данных.

Простейший XML-документ выглядит следующим образом:

<?xml version="1.0" encoding="windows-1251"?>
<book category="WEB">
   <title lang="en">Learning XML</title>
   <author>Erik T. Ray</author>
   <year>2003</year>
   <price></price>
</book>

Первая строка — это XML декларация. Здесь определяется версия XML (1.0) и кодировка файла. На следующей строке описывается корневой элемент документа (открывающий тег). Следующие 4 строки описывают дочерние элементы корневого элемента ( , , , ). Последняя строка определяет конец корневого элемента (закрывающий тег).

Документ XML состоит из элементов (elements). Элемент начинается открывающим тегом (start-tag) в угловых скобках, затем идет содержимое (content) элемента, после него записывается закрывающий тег (end-teg) в угловых скобках.

Информация, заключенная между тегами, называется содержимым или значением элемента: . Т.е. элемент принимает значение . Элементы могут вообще не принимать значения.

Элементы могут содержать атрибуты, так, например, открывающий тег имеет атрибут , который принимает значение . Значения атрибутов заключаются в кавычки (двойные или ординарные).

Некоторые элементы, не содержащие значений, допустимо записывать без закрывающего тега. В таком случае символ ставится в конце открывающего тега:

Поясняющий пример

В примере демонстрируется ситуация, когда периодически присоединяется одна строка к другой, так что обе строки должны быть скопированы во временную область (), создана новая строка и, затем, ссылка на оригинальную строку заменяется ссылкой на новую строку.

Но что если вы не выполняете этот тип операции, а вместо этого, просто имеете некоторый код, похожий на следующий:

Здесь нет цикла или повторений и нет строки, которая становится все длиннее и длиннее. Есть какой-либо вред от применения вместо в этом примере?

Для ответа на этот вопрос рассмотрим дополнительный код:

В этой программе создается класс , который используется для представления точек . В ней реализуются различные методы для класса. Результат выполнения программы может выглядеть примерно так:

Члены документа

<param>

name: имя параметра метода. Имя заключается в двойные кавычки (» «). Имена параметров должны соответствовать сигнатуре API. Если один или несколько параметров не охвачены, компилятор выдает предупреждение. Компилятор также выдает предупреждение, если значение name не соответствует формальному параметру в объявлении метода.

name: имя параметра, на который указывается ссылка. Имя заключается в двойные кавычки (» «).

<exception>

cref = «member»: ссылка на исключение, которое доступно из текущей среды компиляции. Компилятор проверяет, существует ли исключение, и приводит member к каноническому имени элемента в выходных XML-данных. member необходимо заключать в двойные кавычки (» «).

Тег служит для указания возможных исключений. Этот тег может применяться к определениям методов, свойств, событий и индексаторов.

<value>

Тег позволяет описывать значение, которое представляется свойством. При добавлении свойства с помощью мастера создания кода из среды разработки Visual Studio .NET для нового свойства будет добавлен тег . Следует вручную добавить тег для описания значения, которое представляется свойством.

Общие теги

<summary>

Тег следует использовать для описания типа или элемента типа. Чтобы добавить дополнительную информацию в описание типа, используйте . Чтобы включить средства документации, такие как DocFX и Sandcastle, для создания внутренних гиперссылок на страницы документации для элементов кода, используйте атрибут . Текст в теге является единственным источником сведений о типе для технологии IntelliSense и также отображается в окне обозревателя объектов.

<remarks>

Тег позволяет добавить сведения о типе или члене типа, дополняющие информацию, указанную с помощью тега . Эти сведения отображаются в окне «Обозреватель объектов». Этот тег может содержать более длинные объяснения. Возможно, вы обнаружите, что использование разделов для разметки упрощает написание. Такие средства, как docfx, обрабатывают текст разметки в разделах .

Комментарии

Комментарии, противоречащие коду, хуже, чем отсутствие комментариев. Всегда исправляйте комментарии, если меняете код!

Комментарии должны являться законченными предложениями. Если комментарий — фраза или предложение, первое слово должно быть написано с большой буквы, если только это не имя переменной, которая начинается с маленькой буквы (никогда не изменяйте регистр переменной!).

Если комментарий короткий, можно опустить точку в конце предложения. Блок комментариев обычно состоит из одного или более абзацев, составленных из полноценных предложений, поэтому каждое предложение должно оканчиваться точкой.

Ставьте два пробела после точки в конце предложения.

Программисты, которые не говорят на английском языке, пожалуйста, пишите комментарии на английском, если только вы не уверены на 120%, что ваш код никогда не будут читать люди, не знающие вашего родного языка.

Блоки комментариев

Блок комментариев обычно объясняет код (весь, или только некоторую часть), идущий после блока, и должен иметь тот же отступ, что и сам код. Каждая строчка такого блока должна начинаться с символа # и одного пробела после него (если только сам текст комментария не имеет отступа).

Абзацы внутри блока комментариев разделяются строкой, состоящей из одного символа #.

«Встрочные» комментарии

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

Такой комментарий находится в той же строке, что и инструкция. «Встрочные» комментарии должны отделяться по крайней мере двумя пробелами от инструкции. Они должны начинаться с символа # и одного пробела.

Комментарии в строке с кодом не нужны и только отвлекают от чтения, если они объясняют очевидное. Не пишите вот так:

x = x + 1                 # Increment x

Впрочем, такие комментарии иногда полезны:

x = x + 1                 # Компенсация границы

Кодировки¶

И еще один важный момент, который стоит рассмотреть — кодировки. Существует множество кодировок, о них подробнее можно прочитать в статье Набор
символов.

Самыми распространенными кириллическими кодировками являются и . Последняя является одним из стандартов, но большая часть ФНС отчетности имеет кодировку .

В XML файле кодировка объявляется в декларации:

<?xml version="1.0" encoding="windows-1251"?>

Часто можно столкнуться с ситуацией, когда текстовый редаткор некорректно распознает кодировку и отображает кракозябры. В такой случае, необходимо выбрать кодировку вручную, для этого выполните:

Таблица I.3 — Смена кодировки в разных программах
Программа Кодировка
Notepad++ «Документ → Кодировка»
Geany «Документ → Установить кодировку»
Firefox «Вид → Кодировка»
Chrome «Настройка → Дополнительные инструменты → Кодировка»

Используйте класс Objects и библиотеку StringUtils

Ошибки нередко прокрадываются в базу кода, когда у нас нет ясного понимания того, что код делает или почему он это делает

Здесь важно удобство в обслуживании. Я часто сталкиваюсь с запутанным кодом создания строки

По какой-то причине люди склонны увлекаться тернарными выражениями, помещая их в другие выражения или вкладывая друг в друга. Это очень эффективно усложняет чтение и понимание простой логики.

Тернарный оператор часто используется для возвращения предустановленного значения, когда переменная содержит . Ниже приведен пример этого, взятый прямо из нашей базы кода Columna:

String s = (form != null ? form : "-") + " " + (nameAndStrength != null ? nameAndStrength: "");

Примечательно, что это не самый сложный для анализа код, но я думаю, что его можно написать гораздо лучше. В C# существует так называемый оператор объединения с неопределенным значением, относящийся к тернарному оператору, явно проверяющему значение на . Вот пример:

String s = form ?? "-" + " " + nameAndStrength ?? "-";

Здесь левое значение оператора присваивается, если оно не , в противном случае используется правая сторона. Несмотря на то, что в Java недостает подобного оператора, с той же целью можно использовать встроенный класс , описанный выше. Метод класса возвращает значение первого объекта, если этот объект не , в противном случае он возвращает указанное строковое значение.

String hyphen = "";String s = Objects.toString(form, hyphen) + " " + Objects.toString(nameAndStrength, hyphen);

Не так складно, как в примере с C#, но, на мой взгляд, намного надежнее, чем исходный пример, а значит и шанс появления ошибок при работе с кодом уже существенно меньше.

XML-документы

Последнее обновление: 14.10.2019

На сегодняшний день XML является одним из распространенных стандартов документов, который позволяет в удобной форме сохранять сложные по структуре данные.
Поэтому разработчики платформы .NET включили в фреймворк широкие возможности для работы с XML.

Прежде чем перейти непосредственно к работе с XML-файлами, сначала рассмотрим, что представляет собой xml-документ и как он может хранить объекты, используемые в программе на c#.

Например, у нас есть следующий класс:

class User
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Company { get; set; }
}

В программе на C# мы можем создать список объектов класса User:

User user1 = new User { Name = "Bill Gates", Age = 48, Company = "Microsoft" };
User user2 = new User { Name = "Larry Page", Age = 42, Company = "Google" };
List<User> users = new List<User> { user1, user2 };

Чтобы сохранить список в формате xml мы могли бы использовать следующий xml-файл:

<?xml version="1.0" encoding="utf-8" ?>
<users>
  <user name="Bill Gates">
    <company>Microsoft</company>
    <age>48</age>
  </user>
  <user name="Larry Page">
    <company>Google</company>
    <age>48</age>
  </user>
</users>

XML-документ объявляет строка . Она задает версию (1.0) и кодировку (utf-8) xml. Далее идет
собственно содержимое документа.

XML-документ должен иметь один единственный корневой элемент, внутрь которого помещаются все остальные элементы. В данном случае таким элементом является
элемент . Внутри корневого элемента задан набор элементов . Вне корневого элемента
мы не можем разместить элементы .

Каждый элемент определяется с помощью открывающего и закрывающего тегов, например, и , внутри которых
помещается значение или содержимое элементов. Также элемент может иметь сокращенное объявление: — в конце элемента помещается слеш.

Элемент может иметь вложенные элементы и атрибуты. В данном случае каждый элемент user имеет два вложенных элемента и
и атрибут .

Атрибуты определяются в теле элемента и имеют следующую форму: . Например, ,
в данном случае атрибут называется и имеет значение

Внутри простых элементов помещается их значение. Например, — элемент имеет значение
.

Названия элементов являются регистрозависимыми, поэтому и будут представлять разные элементы.

Таким образом, весь список Users из кода C# сопоставляется с корневым элементом , каждый объект User — с элементом ,
а каждое свойство объекта User — с атрибутом или вложенным элементом элемента

Что использовать для свойств — вложенные элементы или атрибуты? Это вопрос предпочтений — мы можем использовать как атрибуты, так и вложенные элементы.
Так, в предыдущем примере вполне можно использовать вместо атрибута вложенный элемент:

<?xml version="1.0" encoding="utf-8" ?>
<users>
  <user>
	<name>Bill Gates</name>
    <company>Microsoft</company>
    <age>48</age>
  </user>
  <user>
	<name>Larry Page</name>
    <company>Google</company>
    <age>48</age>
  </user>
</users>

Теперь рассмотрим основные подходы для работы с XML, которые имеются в C#.

НазадВперед

XSD схема¶

XML Schema — язык описания структуры XML-документа, его также называют XSD. Как большинство языков описания XML, XML Schema была задумана для определения правил, которым должен подчиняться документ. Но, в отличие от других языков, XML Schema была разработана так, чтобы её можно было использовать в создании программного обеспечения для обработки документов XML.

После проверки документа на соответствие XML Schema читающая программа может создать модель данных документа, которая включает:

  • словарь (названия элементов и атрибутов);
  • модель содержания (отношения между элементами и атрибутами и их структура);
  • типы данных.

Каждый элемент в этой модели ассоциируется с определённым типом данных, позволяя строить в памяти объект, соответствующий структуре XML-документа. Языкам объектно-ориентированного программирования гораздо легче иметь дело с таким объектом, чем с текстовым файлом.

Подробнее об XSD смотрите:

  • XML Schema
  • XSD — умный XML

Примечание

Примером использования XSD cхем может служить электронная отчетность:

ФНС: Справочник налоговой и бухгалтерской отчетности

Правила экранирования

SQL Server , содержащие недопустимые в именах XML символы, например пробелы, преобразуются в имена XML таким образом, при котором недопустимые символы переводятся в представление соответствующими численными сущностями с экранирующим символом.

Есть только два небуквенных символа, которые могут встречаться в имени XML: двоеточие (:) и подчеркивание (_). Так как двоеточие уже зарезервировано для пространств имен, в качестве escape-символа используется подчеркивание. Далее перечислены правила кодирования недопустимых символов.

Все символы UCS-2, которые не могут входить имена XML в соответствии со стандартом XML 1.0, экранируются как _xHHHH_. Здесь сочетание HHHH будет замещено на четырехразрядный шестнадцатеричный код символа в UCS-2, где самым старшим является первый разряд. Например, имя таблицы Order Details будет закодировано как Order_x0020_Details.

Символы, не входящие в диапазон UCS-2 (дополнения UCS-4 в диапазоне от U+00010000 до U+0010FFFF), кодируются как _xHHHHHHHH_. Здесь сочетание HHHHHHHH будет замещено на восьмиразрядный шестнадцатеричный код символа в UCS-4, если используется режим обратной совместимости с SQL Server 2000. В иных случаях символы кодируются как _xHHHHHH_в целях соответствия стандарту ISO.

Символ нижнего подчеркивания не нуждается в экранировании, если за ним не следует символ x. Например, имя таблицы Данные заказа не будет закодировано.

Двоеточие в идентификаторах не кодируется. Поэтому запрос FOR XML может формировать элемент пространства имени и имена атрибутов

Например, следующий запрос формирует атрибут пространства имен с двоеточием в имени:

Результат этого запроса будет таким:

Обратите внимание, что для добавления пространств имен XML рекомендуется использовать предложение WITH XMLNAMESPACES.

Используйте StringUtils для защиты String-методов от null

Библиотека StringUtils упрощает обработку и пустых строк. В API это описывается как: “Защищенные от null операции со String.” Эта библиотека содержит много стандартных строковых операций со встроенными проверками на . При ее использовании база кода станет менее громоздкой, так как вам не потребуется реализовывать весь рутинный код проверок на , и читать условные выражения станет легче

При этом база кода также станет более единообразной, и что более важно, код станет безопаснее, так как вы уже ненароком не забудете реализовать проверки нулевых значений. В частности, такая агрессивная защита выражений конкатенации с условными инструкциями на основе метода гарантирует, что в строки уже не проскользнут нежелательные “null”-значения

Заключение

Вкратце содержание рассмотренной серии статей можно выразить так:

  • В ряде случаев применение допустимо. Не бойтесь его использовать там, где он имеет смысл как часть моделирования заданной области.
  • Минимизируйте использование и не применяйте там, где другие его не ждут — следите за интерфейсом между методами.
  • Не задействуйте для неявного указания ошибок — лучше выбрасывайте явное исключение.
  • Используйте перегрузку метода и шаблон для избежания передачи -значений в вызовы метода.
  • Никогда не присваивайте переменной типа коллекции значение — используйте для создания пустых структур данных.
  • При возврате пустых значений используйте вместо , чтобы вызывающие ожидали пустое значение и обеспечивали для него соответствующую обработку.
  • При невозможности применить — явно выразите ваши намерения обработки/не обработки -параметров и возвращения/не возвращения в методах при помощи аннотаций .
  • Используйте для намеренного провала при нежелательных -параметрах.
  • Высматривайте коварные -строки и используйте класс и библиотеку для изящной обработки .

Читайте нас в Telegram, VK и

  • Java. Вложенные классы
  • Портируем решатель судоку с Java на WebAssembly
  • Синхронизация в Java. Часть 1

Читайте нас в Telegram, VK и

Проблемы кодирования

Эта ошибка может быть вызвана следующими причинами.

  • Несовпадение регистра в исходном коде или файле определения модуля (DEF). Например, если вы назначите переменную в одном исходном файле C++ и попытаетесь получить к ней доступ , как в другой, возникает эта ошибка. Чтобы устранить эту проблему, используйте согласованное написание имен и имена регистров.

  • Проект, использующий встраивание функций. Это может произойти при определении функций в исходном файле, а не в файле заголовка. Встроенные функции не отображаются за пределами исходного файла, который их определяет. Чтобы устранить эту проблему, определите встроенные функции в заголовках, где они объявляются.

  • Вызов функции C из программы на языке C++ без использования объявления для функции C. Компилятор использует разные внутренние соглашения об именовании символов для кода C и C++. Внутреннее имя символа — это то, что ищет компоновщик при разрешении символов. Чтобы устранить эту проблему, используйте обертку для всех объявлений функций C, используемых в коде C++, в результате чего компилятор должен использовать внутреннее соглашение об именовании языка c для этих символов. Параметры компилятора /TP и /TC заставляют компилятор компилировать файлы как C++ или C соответственно, независимо от расширения имени файла. Эти параметры могут привести к тому, что имена внутренних функций отличаются от предполагаемых.

  • Попытка сослаться на функции или данные, у которых нет внешней компоновки. В C++ встроенные функции и данные имеют внутреннюю компоновку, если явно не указано в качестве . Чтобы устранить эту проблему, используйте явные объявления для символов, которые ссылаются вне определяющего исходного файла.

  • Отсутствует тело функции или определение переменной . Эта ошибка часто возникает при объявлении, но не определении, переменных, функций или классов в коде. Компилятору требуется только прототип функции или объявление переменной, чтобы создать объектный файл без ошибок, но компоновщик не может разрешить вызов функции или ссылку на переменную, так как код функции или переменное пространство не зарезервированы. Чтобы устранить эту проблему, обязательно Определите каждую указанную функцию и переменную в исходном файле или библиотеке, на которую вы связываетесь.

  • Вызов функции, который использует типы возвращаемых значений и параметров или соглашения о вызовах, которые не соответствуют объектам в определении функции. В объектных файлах C++ декорирование имен кодирует соглашение о вызовах, область класса или пространства имен, а также типы возвращаемых данных и параметров функции. Закодированная строка становится частью окончательного декорированного имени функции. Это имя используется компоновщиком для разрешения или сопоставления вызовов функции из других объектных файлов. Чтобы устранить эту проблему, убедитесь, что в объявлении функции, определении и вызовах используются одни и те же области, типы и соглашения о вызовах.

  • Код C++, который вызывается при включении прототипа функции в определение класса, но не включает реализацию функции. Чтобы устранить эту проблему, обязательно предоставьте определение для всех членов класса, которые вы вызываете.

  • Попытка вызвать чисто виртуальную функцию из абстрактного базового класса. Чистая виртуальная функция не имеет реализации базового класса. Чтобы устранить эту проблему, убедитесь, что все вызванные виртуальные функции реализованы.

  • Попытка использовать переменную, объявленную в функции (Локальная переменная), за пределами области этой функции. Чтобы устранить эту проблему, удалите ссылку на переменную, которая не находится в области действия, или переместите переменную в область более высокого уровня.

  • При построении окончательной версии проекта ATL создается сообщение о том, что код запуска CRT является обязательным. Чтобы устранить эту проблему, выполните одно из следующих действий.

    • Удалите из списка определений препроцессора, чтобы разрешить включение кода запуска CRT. Дополнительные сведения см. в разделе Страница свойств General (Project).

    • Если это возможно, удалите вызовы функций CRT, требующих код запуска CRT. Вместо этого используйте эквиваленты Win32. Например, используйте вместо . Известными функциями, требующими код запуска CRT, являются некоторые функции строк и вычислений с плавающей запятой.

2 ответа

Лучший ответ

На самом деле это не ошибка, хотя может показаться удивительным. Это определенно проблема конкатенации.

Объединение на уровне типа происходит только с типами литералов шаблона и только иногда

Во-первых: конкатенация с не выполняется на уровне типа. имеет тип в TypeScript, а не тип . Некоторое время назад на странице microsoft / TypeScript # 12940 появилось предложение о поддержке конкатенации с , и люди время от времени , но это так и не было реализовано.

Во-вторых: конкатенация с строками литералов шаблона выполняется только иногда выполняется на уровне шрифта. В зависимости от того, где он используется, будет либо , либо . Вы можете получить объединение на уровне типа с помощью (как реализовано в microsoft / TypeScript # 40707 ), или если строка литерала шаблона используется в месте, которое контекстно ожидает объединенную версию (как реализовано в microsoft / TypeScript # 43376).

Это дает нам следующее поведение:

Проверка лишних свойств не выполняется с вычисляемыми ключами типа

TypeScript имеет , что означает, что значение можно присвоить типу , если содержит все члены . Не имеет значения, есть ли у больше участников:

С точки зрения системы типов лишние свойства — это нормально.

Но также существует . Когда вы используете литерал объекта непосредственно в месте, которое ожидает меньшее количество свойств, это считается потенциальной ошибкой, потому что вы выбрасываете информацию:

Но проверка лишних свойств не запускает для вычисленных свойств типа . См. microsoft / TypeScript # 36920; кажется (хотя никто не сказал об этом явно), что это не ошибка, и просьба изменить ее считается запросом функции. (См. Также ). Так что на данный момент у нас есть следующее поведение:

Отсутствие конкатенации на уровне типа + отсутствие лишних проверок свойств на вычисленных ключах приводит к поведению, которое вы видите здесь. Но я понимаю, почему следующее может вызывать удивление:

Первый не выдает ошибки, но по той же причине, что и второй. они оба считаются -значными ключами, поэтому ни одно из них не считается лишним свойством. Так что первая ️ — случайность.

Если вы хотите, чтобы компилятор рассматривал их как типы литералов шаблона, вы можете использовать утверждения :

И теперь оба ️ являются законными.

3

jcalz
27 Авг 2021 в 22:24

Да вроде баг. Похоже, машинописный текст отказывается от вычисления точного типа , когда он используется в качестве ключа объекта, просто притворяется, что это , и вызывает его. Вот почему-то такой код

Ошибок не вызывает, похоже, это ближе к сути ошибки. Если вы заставите TS правильно вычислить тип ключа, хотя

Показывает ошибку.

Но, к сожалению, это не работает со случаем

2

Alex Chashin
27 Авг 2021 в 19:30

Заключение

Вкратце содержание рассмотренной серии статей можно выразить так:

  • В ряде случаев применение допустимо. Не бойтесь его использовать там, где он имеет смысл как часть моделирования заданной области.
  • Минимизируйте использование и не применяйте там, где другие его не ждут  —  следите за интерфейсом между методами. 
  • Не задействуйте для неявного указания ошибок  —  лучше выбрасывайте явное исключение. 
  • Используйте перегрузку метода и шаблон для избежания передачи -значений в вызовы метода. 
  • Никогда не присваивайте переменной типа коллекции значение  —  используйте для создания пустых структур данных. 
  • При возврате пустых значений используйте вместо , чтобы вызывающие ожидали пустое значение и обеспечивали для него соответствующую обработку. 
  • При невозможности применить  —  явно выразите ваши намерения обработки/не обработки -параметров и возвращения/не возвращения в методах при помощи аннотаций .
  • Используйте для намеренного провала при нежелательных -параметрах.
  • Высматривайте коварные -строки и используйте класс и библиотеку для изящной обработки .
  • Java. Вложенные классы
  • Портируем решатель судоку с Java на WebAssembly
  • Синхронизация в Java. Часть 1

Читайте нас в Telegram, VK и

Перевод статьи Jens Christian B. Madsen: Part 2: Avoiding Null-Pointer Exceptions in a Modern Java Application

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Все про сервера
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: