Работаем с XML в Java - первые шаги

В предыдущей части я рассказал тебе общие принципы работы технологии jaxb. Сегодня я покажу как за 5 простых шагов создать тест для xml-выдачи поиска Яндекса.

Стандартную xml-выдачу можно увидеть по этой ссылке. Первое, что нам нужно сделать - описать для неё xsd-схему. Если ты уже заглянул в ссылку, наверняка заметил, что в xml’e приходит очень много разной информации. Описывать всё это вручную - полная печаль.

Шаг первый: берём в руки утилиту trang и используем её по прямому назначению:

trang searchresults.xml searchresults.xsd

Схема готова - поместим её в стандартную папку resources и приступим к генерации java-классов.

Шаг второй: подключаем mava-jaxb плагин.

	<build>
		<plugins>
			<plugin>
				<groupId>org.jvnet.jaxb2.maven2</groupId>
				<artifactId>maven-jaxb2-plugin</artifactId>
				<version>0.8.2</version>
				<executions>
					<execution>
						<goals>
							<goal>generate</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

Пробуем запустить:

mvn clean compile

И получаем ошибку =)

Two declarations cause a collision in the ObjectFactory class.

Проблема в следующем: при генерации классов, помимо самих классов, создаётся еще и фабричный класс ObjectFactory. Этот класс содержит методы для создания классов, с которыми мы будем работать. Имена этих методов генерируются на основе имени элемента/типа в xsd-схеме и иногда они получаются одинаковыми.

Исправить это можно двумя способами. Первый - подправить xsd-схему. Второй - описать jaxb-биндинг, в котором явно указать имена методов для конкретных элементов/типов. Мы пойдём вторым путём =).

Шаг третий(опциональный): добавляем кастомный биндинг.

<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  version="1.0">
  <jaxb:bindings schemaLocation="searchresults.xsd">
    <jaxb:bindings node="//xs:element[@name='_MimeType']">
      <jaxb:factoryMethod name="MimeType"/>
    </jaxb:bindings>
    <jaxb:bindings node="//xs:element[@name='mime-type']">
      <jaxb:factoryMethod name="MimeType2"/>
    </jaxb:bindings>
  </jaxb:bindings>
</jaxb:bindings>

Сохраняем этот файл с расширением .xjb рядом с нашей xsd-схемой в resources. И пробуем снова:

mvn clean compile

И на этот раз уже видим в папке target/generated-sources/xjc/generated 23 файла с packag’ем generated. Почти то, что нужно, осталось поменять package.

Шаг четвертый: правильный package. Для этого добавим секцию конфигурации в описание maven-jaxb плагина в pom.xml:

<configuration>
	<generatePackage>ru.yandex.qatools.xmlsearch.beans</generatePackage>
</configuration>

Осталось всё это заиспользовать в нашем тесте. Шаг пятый: используем.

URL searchResultUrl = CommonXmlSearchTest.class.getResource("/searchresults.xml");

@Test
public void testSimpleRequest() throws JAXBException, IOException {
    int resultSize = from(searchResultUrl).getResponse().getResults().getGrouping().getGroup().size();
    assertThat(resultSize, is(10));
}

public static Yandexsearch from(URL url) throws JAXBException, IOException {
    return unmarshal(Yandexsearch.class, url);
}

public static <T> T unmarshal(Class<T> clazz, URL xml) throws JAXBException, IOException {
    JAXBContext jc = JAXBContext.newInstance(clazz);
    Unmarshaller u = jc.createUnmarshaller();
    // U can use IOUtils.toInputStream(xml) if add to deps commons-io and parse String
    // Also, it can be Node, String, etc
    return u.unmarshal(new StreamSource(xml.openStream()), clazz).getValue();
}

Как видишь, получилось дерево java-объектов полностью идентичное полученному xml-файлу. И при этом весь парсинг xml находится в трёх строчках кода - не надо ни самому описывать объекты, ни следить за их актуальностью. Полный код примера как обычно можно найти у меня на гитхабе =)

comments powered by Disqus