Вступление

Здравствуйте, дорогие читатели моей статьи. Это уже вторая статья из цикла про XML, и в данной статье будет рассказывать про XML Namespace и XML Schema.
Основы XML
Буквально недавно, мне самому ничего про это известно не было, однако я осилил немало материала и буду пытаться объяснить простыми словами эти две важные темы. Сразу хочу сказать, что схемы – очень продвинутый механизм валидации XML документов и значительно более функциональный, чем DTD, потому полного его изучения от и до тут не будет. Давайте приступать :)

XML Namespace

Namespace значит «пространство имён», однако в этой статье я буду часто подменивать русское выражение на просто namespace, ибо это короче и комфортнее для понимания. XML Namespace – это технология, основная цель которой - сделать так, чтобы все элементы были уникальными в XML файле и не было путаницы. И так, как это Java курсы, то такая же технология есть и в Java – пакеты. Если бы можно было поместить два класса с одинаковым именем рядом и использовать их, то как мы бы определили, какой класс нам нужен? Эта проблема решена пакетами – мы можем просто разместить классы в разные пакеты и импортировать их оттуда, точно указав имя нужного пакета и путь к нему, или просто указав полный путь к нужному классу. Основы XML для Java программиста. Часть 2 из 3 - 1Теперь, мы можем сделать так:
public class ExampleInvocation {
    public static void main(String[] args) {
        // Создание экземпляра класса из первого пакета.
        example_package_1.Example example1 = new example_package_1.Example();

        // Создание экземпляра класса из второго пакета.
        example_package_2.Example example2 = new example_package_2.Example();

        // Создание экземпляра класса из третьего пакета.
        example_package_3.Example example3 = new example_package_3.Example();
    }
}
В XML Namespace все примерно так же, только немного по-другому. Суть такая же: если элементы одинаковые (как классы), то мы просто должны использовать их в разных namespace’ах (указывать пакеты), тогда даже если имена элементов (классов) станут совпадать, мы все равно будем обращаться к конкретному элементу из пространства (пакета). Для примера: у нас в XML есть два элемента – предсказание (oracle) и БД Oracle.
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <oracle>
        <connection value="jdbc:oracle:thin:@10.220.140.48:1521:test1" />
        <user value="root" />
        <password value="111" />
    </oracle>

    <oracle>
        Сегодня вы будете заняты весь день.
    </oracle>
</root>
И когда мы будем обрабатывать данный XML файл, мы будем серьезно запутаны, если вместо базы данных нам придет предсказание, и обратно тоже. Для того, чтобы разрешить коллизию элементов, мы можем каждому из них выделить своё собственное пространство, чтобы различать их. Для этого есть специальный атрибут – xmlns:префикс= «уникальное значение для namespace”. После чего, мы можем использовать префикс перед элементами, чтобы указывать, что он является частью этого namespace (по сути, мы должны создать путь к пакету - namespace, а потом перед каждым элементом указывать префиксом, к какому пакету он принадлежит).
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <database:oracle xmlns:database="Unique ID #1">
        <connection value="jdbc:oracle:thin:@10.220.140.48:1521:test1" />
        <user value="root" />
        <password value="111" />
    </database:oracle>

    <oracle:oracle xmlns:oracle="Unique ID #2">
        Сегодня вы будете заняты весь день.
    </oracle:oracle>
</root>
В данном примере мы объявили два пространства имён: database и oracle. Теперь перед элементами можно использовать префиксы namespace’ов. Не нужно пугаться, если сейчас что-то неясно. На самом деле – это очень просто. Сначала, я хотел написать эту часть статьи более быстро, однако после среды я решил, что нужно уделить больше внимания данной теме, так как тут легко запутаться или в чем-то не разобраться. Сейчас будет очень много внимания уделено атрибуту xmlns. И так, еще пример:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="https://www.standart-namespace.com/" xmlns:gun="https://www.gun-shop.com/" xmlns:fish="https://www.fish-shop.com/">
    <gun:shop>
        <gun:guns>
            <gun:gun name="Revolver" price="1250$" max_ammo="7" />
            <gun:gun name="M4A1" price="3250$" max_ammo="30" />
            <gun:gun name="9mm Pistol" price="450$" max_ammo="12" />
        </gun:guns>
    </gun:shop>

    <fish:shop>
        <fish:fishes>
            <fish:fish name="Shark" price="1000$" />
            <fish:fish name="Tuna" price="5$" />
            <fish:fish name="Capelin" price="1$" />
        </fish:fishes>
    </fish:shop>
</root>
Вы можете видеть обычный XML, где используются пространства gun для уникальных элементов оружейного магазина и fish для уникальных элементов рыболовного магазина. Можно увидеть, что создав пространства, мы использовали один элемент shop сразу к двум разным вещам – магазину оружия и магазину рыбы, и нам точно известно, что это за магазин, благодаря тому, что объявили пространства. Самое интересно начнется в схемах, когда мы сможем таким образом еще валидировать разные структуры с одними элементами. xmlns – атрибут для объявления namespace’а, указывать его можно в любом элементе. Пример объявления namespace’а:
xmlns:shop= «https://barber-shop.com/»
После двоеточия находится префикс – это ссылка на пространство, которая потом может использоваться перед элементами, чтобы указывать, что они родом из этого пространства. Значение xmlns должно быть УНИКАЛЬНОЙ СТРОКОЙ. Это крайне важно понимать: очень часто используются ссылки на сайты или URI, чтобы объявить namespace. Это правило является стандартом, так как URI или URL ссылки являются уникальными, НО именно данный момент очень запутывает. Просто запомните: значением может быть ЛЮБАЯ строка, какая вы захотите, но для точной уникальности и стандарта нужно использовать URL или URI адреса. То, что можно использовать любые строки, показано в примере в oracle:
xmlns:oracle="Unique ID #2"
xmlns:database="Unique ID #1"
Когда вы объявляете namespace, вы можете его использовать в самом элементе и во всех элементах внутри него, потому объявленные в root элементе namespace’ы можно использовать во всех элементах. Это можно видеть в последнем примере, и вот более конкретный пример:
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <el1:element1 xmlns:el1="Element#1 Unique String">
        <el1:innerElement>

        </el1:innerElement>
    </el1:element1>


    <el2:element2 xmlns:el2="Element#2 Unique String">
        <el2:innerElement>

        </el2:innerElement>
    </el2:element2>


    <el3:element3 xmlns:el3="Element#3 Unique String">
        <el3:innerElement>
            <el1:innerInnerElement> <!-- Так нельзя, потому что пространство el1 объявлено только в первом элементе, потому может использовать только внутри первого элемента и его внутренних элементов. -->

            </el1:innerInnerElement>
        </el3:innerElement>
    </el3:element3>
</root>
Тут важная деталь: существует так же стандартный namespace в root элементе. Если вы объявили другие namespace’ы, вы стираете стандартное и не можете его использовать. Тогда перед root элементом нужно поставить какой-то префикс пространства, любой, который вы объявили ранее. Однако, это можно так же обхитрить: вы можете объявить стандартное пространство явно. Достаточно просто не использовать префикс после xmlns, а сразу записать какое-то значение, и все ваши элементы без префикса станут принадлежать именно этому namespace’у. В последнем примере это было использовано:
<root xmlns="https://www.standart-namespace.com/" xmlns:gun="https://www.gun-shop.com/" xmlns:fish="https://www.fish-shop.com/">
Мы объявили стандартное пространство явно, чтобы избежать необходимости использовать gun или fish, так как рут элемент не является сущностью ни рыболовного магазина, ни оружейного, потому использование любого пространства было бы уже логически неправильным. Далее: если вы создали xmlns:a и xmlns:b, но у них одно значение, то это одинаковое пространство и они не уникальные. Потому и нужно использовать всегда уникальные значения, ведь нарушение этого правила может создать большое количество ошибок. Например, если бы у нас было так объявлены пространства:
xmlns="https://www.standart-namespace.com/" xmlns:gun="https://www.gun-shop.com/" xmlns:fish="https://www.gun-shop.com/"
То наш рыболовный магазин стал бы оружейным, а префикс был бы все еще рыбного магазинчика. Это все основные моменты пространств. Я довольно много времени потратил на то, чтобы собрать их все и сократить, а потом ясно выразить, так как информация по пространствам в Интернете очень огромная и часто одна вода, потому большая часть всего, что тут есть – я узнал это сам пробами и ошибками. Если у вас остались вопросы, то можете попробовать ознакомиться с материалами по ссылкам в конце статьи.

XML Schema

Сразу хочу сказать, что в данной статье будет только верхушка айсберга, так как тема очень обширная. Если вы захотите ознакомиться более подробно со схемами и научиться писать их самому любой сложности, то в конце статьи будет ссылка, где будет все про разные типы, ограничения, расширения и так далее. Начать хочу с теории. Схемы обладают форматом .xsd (xml scheme definition) и являются более продвинутой и популярной альтернативой DTD: они способны так же создавать элементы, описывать их и так далее. Однако, добавлено очень много бонусов: проверка типов, поддержка нэймспэйсов и более широкий функционал. Помните, когда мы говорили про DTD, там был минус, что он не поддерживает пространства? Теперь, когда мы это изучили, объясняю: если бы можно было импортировать две и более схемы с DTD, где были бы одинаковые элементы, у нас были бы коллизии (совпадения) и нельзя было бы их использовать вообще, ведь неясно, какой элемент нам нужен. В XSD данная проблема решена, ведь вы можете импортировать схемы в одно конкретное пространство и использовать его. По сути, у каждой XSD схемы есть целевое пространство, которое означает, в какое пространство должна быть записана схема в XML файле. Таким образом, в самом XML файле нам нужно просто создать эти заранее определенные в схемах пространства и назначить префиксы для них, а потом подключить в каждое из них нужные схемы, после чего мы можем спокойно использовать элементы из схемы, подставляя префиксы из того пространства, куда мы импортировали схемы. И так, у нас есть пример:
<?xml version="1.0" encoding="UTF-8"?>
<house>
    <address>ул. Есенина, дом №5</address>
    <owner name="Ivan">
        <telephone>+38-094-521-77-35</telephone>
    </owner>
</house>
Мы хотим валидировать его с помощью схемы. Для начала, нам нужна схема:
<?xml version="1.0"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="https://www.nedvigimost.com/">
    <element name="house">
        <complexType>
            <sequence>
                <element name="address" type="string" maxOccurs="unbounded" minOccurs="0" />
                <element name="owner" maxOccurs="unbounded" minOccurs="0" >
                    <complexType>
                        <sequence>
                            <element name="telephone" type="string" />
                        </sequence>
                        <attribute name="name" type="string" use="required"/>
                    </complexType>
                </element>
            </sequence>
        </complexType>
    </element>
</schema>
Как вы видите, схемы – это тоже XML файлы. Вы прямо на XML языке расписываете то, что вам нужно. Данная схема способна валидировать XML файл из примера выше. Например: если у овнера не будет имени, то схема это увидит. Так же, благодаря элементу sequence, всегда должен идти сначала адрес, а потом владелец дома. Есть элементы обычные и комплексные. Обычные элементы – это элементы, которые хранят в себя только какой-то тип данных. Пример:
<element name="telephone" type="string" />
Так мы объявляем элемент, который хранит в себе строку. Других элементов быть внутри этого элемента не должно. Так же есть комплексные элементы. Комплексные элементы способны хранить внутри себя другие элементы, атрибуты. Тогда тип указывать не нужно, а достаточно внутри элемента начать писать комплексный тип.
<complexType>
    <sequence>
        <element name="address" type="string" maxOccurs="unbounded" minOccurs="0" />
        <element name="owner" maxOccurs="unbounded" minOccurs="0" >
            <complexType>
                <sequence>
                    <element name="telephone" type="string" />
                </sequence>
                <attribute name="name" type="string" use="required"/>
            </complexType>
        </element>
    </sequence>
</complexType>
Так же можно было поступить по-другому: можно было создать комплексный тип отдельно, а потом подставлять его в type. Только во время написания этого примера, почему-то нужно было объявить пространство под каким-то префиксом, а не использовать стандартное. В общем, получилось вот так вот:
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="https://www.nedvigimost.com/">
    <xs:element name="house" type="content" />

    <xs:complexType name="content">
        <xs:sequence>
            <xs:element name="address" type="xs:string" maxOccurs="unbounded" minOccurs="0" />
            <xs:element name="owner" maxOccurs="unbounded" minOccurs="0" >
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="telephone" type="xs:string" />
                    </xs:sequence>
                    <xs:attribute name="name" type="xs:string" use="required"/>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:schema>
Таким образом, мы можем создавать наши собственные типы отдельно, а потом подставлять их куда-либо в атрибут type. Это очень удобно, так как позволяет использовать один тип в разных местах. Хотелось бы еще поговорить про подключение схем и закончить на этом. Есть два способа подключить схему: в конкретное пространство и просто подключить.

Первый способ подключения схемы

Первый способ подразумевает, что у схемы есть конкретное целевое пространство. Оно указывается с помощью атрибута targetNamespace у элемента scheme. Тогда достаточно создать ЭТО САМОЕ пространство в XML файле, после чего «загрузить» туда схему:
<?xml version="1.0" encoding="UTF-8"?>
<nedvig:house xmlns:nedvig="https://www.nedvigimost.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://www.nedvigimost.com/ example_schema1.xsd">
    <address>ул. Есенина, дом №5</address>
    <owner name="Ivan">
        <telephone>+38-094-521-77-35</telephone>
    </owner>
</nedvig:house>
Важно понимать две строчки:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemeLocation="https://www.nedvigimost.com/ example_schema1.xsd"
Первая строчка – просто запомните её. Считайте, что это объект, который помогает загружать схемы туда, куда надо. Вторая строчка – уже конкретная загрузка. schemaLocation принимает список значений вида «значение – значение», разделенные пробелом. Первый аргумент – пространство имён, которое должно соответствовать целевому пространству имён в схеме (значению targetNamespace). Второй аргумент – относительно или абсолютный путь к схеме. И так, как это СПИСОК значение, то вы можете после схемы в примере поставить пробел, и снова ввести целевое пространство и имя другой схемы, и так сколько захотите. Важно: чтобы схема потом валидировала что-либо, вам нужно объявить это пространство и с префиксом использовать. Посмотрите внимательно последний пример:
<?xml version="1.0" encoding="UTF-8"?>
<nedvig:house xmlns:nedvig="https://www.nedvigimost.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://www.nedvigimost.com/ example_schema1.xsd">
    <address>ул. Есенина, дом №5</address>
    <owner name="Ivan">
        <telephone>+38-094-521-77-35</telephone>
    </owner>
</nedvig:house>
Мы создали это целевое пространство на префиксе nedvig, а потом использовали его. Таким образом, наши элементы начали валидироваться, так как мы начали использовать пространство, куда ссылается целевое пространство схемы.

Второй способ подключения схемы

Второй способ подключения схемы подразумевает, что у схемы нет конкретного целевого пространства. Тогда вы можете просто подключить её к XML файлу и она будет валидировать его. Делается почти так же, только вы можете не объявлять пространства вообще в XML файле, а просто подключить схему.
<?xml version="1.0" encoding="UTF-8"?>
<house xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="example_schema1.xsd">
    <address>ул. Есенина, дом №5</address>
    <owner name="Ivan">
        <telephone>+38-094-521-77-35</telephone>
    </owner>
</house>
Как вы видите, делается это с помощью noNamespaceSchemaLocation и указанием пути к схеме. Даже если у схемы нет целевого пространства, документ будет валидироваться. И последний штрих: мы можем импортировать в схемы другие схемы, после чего использовать элементы из одной схемы в другой. Таким образом, мы можем использовать в одних схемах элементы, которые есть уже в других. Пример:

Схема, где объявляется тип owner:

<?xml version="1.0" encoding="UTF-8" ?>
<schema targetNamespace="bonus" xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
        <complexType name="owner">
            <all>
                <element name="telephone" type="string" />
            </all>
            <attribute name="name" type="string" />
        </complexType>
</schema>

Вторая схема, где используется тип owner из первой схемы:

<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="main" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:bonus="bonus" elementFormDefault="qualified">
    <import namespace="bonus" schemaLocation="xsd2.xsd" />
    <element name="house">
        <complexType>
            <all>
              <element name="address" type="string" />
                <element name="owner" type="bonus:owner" />
            </all>
        </complexType>
    </element>
</schema>
Во второй схеме используется конструкция:
<import namespace="bonus" schemaLocation="xsd2.xsd" />
С помощью неё мы импортировали типы и элементы из одной схемы в другую в пространство bonus. Таким образом, мы получили доступ к типу bonus:owner. А в следующей строчке мы его использовали:
<element name="owner" type="bonus:owner" />
Так же небольшое внимание следующей строчке:
elementFormDefault="qualified"
Этот атрибут объявляется в schema и означает, что в XML файлах каждый элемент должен объявляться с явным префиксом перед ним. Если его нет, то нам достаточно объявить внешний элемент с префиксом, а так нужно выставлять префиксы и во всех элементах внутри, явно указывая, что мы используем именно элементы этой схемы. И вот, собственно, пример XML файла, валидируемого схемой, которая импортировала другую схему:
<?xml version="1.0" encoding="UTF-8"?>
<nedvig:house xmlns:nedvig="main" xmlns:bonus="bonus" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="main xsd.xsd">
    <nedvig:address>ул. Есенина, дом №5</nedvig:address>
    <nedvig:owner name="Ivan">
        <bonus:telephone>+38-094-521-77-35</bonus:telephone>
    </nedvig:owner>
</nedvig:house>
В строчке:
<bonus:telephone>+38-094-521-77-35</bonus:telephone>
Нам нужно явно объявлять пространство имён bonus, указывающее на целевое пространство первой схемы, так как elementFormDefault у нас в qualified (проверять), потому все элементы должны явно указывать свое пространство.

Конец статьи

Следующая статья будет последней в цикле и там уже будет про обработку XML файлов средствами Java. Мы будем обучаться доставать информацию разными способами и так далее. Надеюсь, что эта статья была полезной и, даже если тут есть где-то ошибки, научит вас чему-то полезному и новому, а может просто даст возможность лучше понимать XML файлы. Для тех, кто хотел бы изучить это более подробно, я решил собрать небольшой набор ссылок:
  • XSD Simple Elements — начиная с этой статьи начинайте читать и идите вперед, там собрана вся информация по схемам и рассказывается более-менее понятно, только на английском. Можете использовать переводчик.

  • видео по пространствам имён, всегда полезно послушать другую точку зрения на что-либо, если первая не ясна.

  • XML пространства имен - хороший пример использования пространств имён и довольно укомплектованная информация.

  • Основы XML — пространства имен - еще одна небольшая статья по пространствам имён.

  • Основы использования XML Schema для определения элементов — тоже крайне полезная ссылка по схемам, только читать нужно медленно и внимательно, вникая в материал.

На этом уже точно всё, буду надеяться, что если вы захотите узнать что-либо глубже из этого, то ссылки вам помогут. Я сам шастал по всем этим источникам, изучая весь материал, и, в целом, это были самые полезные из всех источников, что я смотрел, так как каждый из них или улучшал понимание того, что я уже прочел где-то в другом месте, или давал узнать что-то новое, однако много было сделано как раз во время практики. Так что, тем, кто реально хочет разобраться во всем этом довольно хорошо, мой совет: изучите пространства имён, после чего то, как просто подключать схемы к XML файлам, а потом уже то, как прописывать структуру документа в схемах. А главное - практикуйтесь. Всем спасибо за внимание и успехов в программировании :) Предыдущая статья: [Конкурс] Основы XML для Java программиста - Часть 1 из 3 Следующая статья: [Конкурс] Основы XML для Java программиста - Часть 3.1 из 3 - SAX