Статьи

Отображение объектов на несколько XML-схем — пример погоды

Я писал предыдущие посты о @XmlPath EclipseLink JAXB (MOXy) и внешних файлах привязки . В этой статье я продемонстрирую, насколько мощными являются эти расширения, сопоставляя одну объектную модель с двумя различными XML-схемами.

Чтобы сделать пример более «реальным», данные XML будут поступать из двух разных служб, предоставляющих информацию о погоде: Google и Yahoo.

Модель Java

Для этого поста будет использоваться следующая модель домена:

Прогноз погоды

01
02
03
04
05
06
07
08
09
10
11
12
package blog.weather;
  
import java.util.List;
  
public class WeatherReport {
  
    private String location;
    private int currentTemperature;
    private String currentCondition;
    private List forecast;
  
}

Прогноз

01
02
03
04
05
06
07
08
09
10
package blog.weather;
  
public class Forecast {
  
    private String dayOfTheWeek;
    private int low;
    private int high;
    private String condition;
  
}

Google Weather API

Сначала мы будем использовать Google Weather API. Следующий URL будет использоваться для доступа к данным о погоде в Оттаве, Канада:

http://www.google.com/ig/api?weather=Ottawa

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<xml_api_reply version="1">
    <weather module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1"
        row="0" section="0">
        <forecast_information>
            <city data="Ottawa, ON" />
            <postal_code data="Ottawa" />
            <latitude_e6 data="" />
            <longitude_e6 data="" />
            <forecast_date data="2011-09-08" />
            <current_date_time data="2011-09-08 14:00:00 +0000" />
            <unit_system data="US" />
        </forecast_information>
        <current_conditions>
            <condition data="Mostly Cloudy" />
            <temp_f data="66" />
            <temp_c data="19" />
            <humidity data="Humidity: 73%" />
            <icon data="/ig/images/weather/mostly_cloudy.gif" />
            <wind_condition data="Wind: NE at 13 mph" />
        </current_conditions>
        <forecast_conditions>
            <day_of_week data="Thu" />
            <low data="55" />
            <high data="75" />
            <icon data="/ig/images/weather/cloudy.gif" />
            <condition data="Cloudy" />
        </forecast_conditions>
        <forecast_conditions>
            <day_of_week data="Fri" />
            <low data="46" />
            <high data="77" />
            <icon data="/ig/images/weather/mostly_sunny.gif" />
            <condition data="Partly Sunny" />
        </forecast_conditions>
        <forecast_conditions>
            <day_of_week data="Sat" />
            <low data="43" />
            <high data="68" />
            <icon data="/ig/images/weather/sunny.gif" />
            <condition data="Clear" />
        </forecast_conditions>
        <forecast_conditions>
            <day_of_week data="Sun" />
            <low data="55" />
            <high data="75" />
            <icon data="/ig/images/weather/sunny.gif" />
            <condition data="Clear" />
        </forecast_conditions>
    </weather>
</xml_api_reply>

Модель Java — сопоставлена ​​с XML-схемой Google с помощью аннотаций

Мы отобразим результат API погоды Google с помощью комбинации стандартных аннотаций расширений JAXB и MOXy.

Прогноз погоды

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package blog.weather;
  
import java.util.List;
  
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
  
import org.eclipse.persistence.oxm.annotations.XmlPath;
  
@XmlRootElement(name="xml_api_reply")
@XmlType(propOrder={"location", "currentCondition", "currentTemperature", "forecast"})
@XmlAccessorType(XmlAccessType.FIELD)
public class WeatherReport {
  
    @XmlPath("weather/forecast_information/city/@data")
    private String location;
  
    @XmlPath("weather/current_conditions/temp_f/@data")
    private int currentTemperature;
  
    @XmlPath("weather/current_conditions/condition/@data")
    private String currentCondition;
  
    @XmlPath("weather/forecast_conditions")
    private List<Forecast> forecast;
  
}

Прогноз

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
package blog.weather;
  
import org.eclipse.persistence.oxm.annotations.XmlPath;
  
public class Forecast {
  
    @XmlPath("day_of_week/@data")
    private String dayOfTheWeek;
  
    @XmlPath("low/@data")
    private int low;
  
    @XmlPath("high/@data")
    private int high;
  
    @XmlPath("condition/@data")
    private String condition;
  
}

демонстрация

Следующий демонстрационный код будет читать данные XML для службы погоды Google и перенаправлять объекты обратно в XML:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
package blog.weather;
  
import java.net.URL;
import javax.xml.bind.*;
  
public class GoogleDemo {
  
    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(WeatherReport.class);
  
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        URL url = new URL("http://www.google.com/ig/api?weather=Ottawa");
        WeatherReport weatherReport = (WeatherReport) unmarshaller.unmarshal(url);
  
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(weatherReport, System.out);
    }
  
}

Выход

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="UTF-8"?>
<xml_api_reply>
   <weather>
      <forecast_information>
         <city data="Ottawa, ON"/>
      </forecast_information>
      <current_conditions>
         <condition data="Mostly Cloudy"/>
         <temp_f data="68"/>
      </current_conditions>
      <forecast_conditions>
         <day_of_week data="Thu"/>
         <low data="55"/>
         <high data="75"/>
         <condition data="Cloudy"/>
      </forecast_conditions>
      <forecast_conditions>
         <day_of_week data="Fri"/>
         <low data="46"/>
         <high data="77"/>
         <condition data="Partly Sunny"/>
      </forecast_conditions>
      <forecast_conditions>
         <day_of_week data="Sat"/>
         <low data="43"/>
         <high data="68"/>
         <condition data="Clear"/>
      </forecast_conditions>
      <forecast_conditions>
         <day_of_week data="Sun"/>
         <low data="55"/>
         <high data="75"/>
         <condition data="Clear"/>
      </forecast_conditions>
   </weather>
</xml_api_reply>

Yahoo Weather API

Следующий URL-адрес будет использоваться для доступа к данным о погоде для Оттавы с помощью API погоды Yahoo (3369 — это WOEID для Оттавы):

http://weather.yahooapis.com/forecastrss?w=3369

Ниже приведен результат выполнения вышеуказанного запроса в то время, когда я писал эту статью:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0"
    <channel>
        <title>Yahoo! Weather - Ottawa, CA</title>
        <description>Yahoo! Weather for Ottawa, CA</description>
        <language>en-us</language>
        <lastBuildDate>Thu, 08 Sep 2011 10:58 am EDT</lastBuildDate>
        <ttl>60</ttl>
        <yweather:location city="Ottawa" region="ON"
            country="Canada" />
        <yweather:units temperature="F" distance="mi" pressure="in"
            speed="mph" />
        <yweather:wind chill="66" direction="40" speed="12" />
        <yweather:atmosphere humidity="73" visibility=""
            pressure="30.14" rising="0" />
        <yweather:astronomy sunrise="6:31 am" sunset="7:25 pm" />
        <image>
            <title>Yahoo! Weather</title>
            <width>142</width>
            <height>18</height>
            <link>http://weather.yahoo.com</link>
        </image>
        <item>
            <title>Conditions for Ottawa, CA at 10:58 am EDT</title>
            <geo:lat>45.42</geo:lat>
            <geo:long>-75.69</geo:long>
            <pubDate>Thu, 08 Sep 2011 10:58 am EDT</pubDate>
            <yweather:condition text="Mostly Cloudy" code="28"
                temp="66" date="Thu, 08 Sep 2011 10:58 am EDT" />
            <description><![CDATA[
  
<b>Current Conditions:</b>
  
Mostly Cloudy, 66 F
  
  
<b>Forecast:</b>
  
Thu - Partly Cloudy. High: 75 Low: 57
  
Fri - Partly Cloudy. High: 79 Low: 53
  
  
  
  
  
(provided by <a href="http://www.weather.com" >The Weather Channel</a>)
  
]]></description>
            <yweather:forecast day="Thu" date="8 Sep 2011" low="57"
                high="75" text="Partly Cloudy" code="30" />
            <yweather:forecast day="Fri" date="9 Sep 2011" low="53"
                high="79" text="Partly Cloudy" code="30" />
            <guid isPermaLink="false">CAXX0343_2011_09_09_7_00_EDT</guid>
        </item>
    </channel>
</rss><!-- api4.weather.sp2.yahoo.com uncompressed/chunked Thu Sep 8 08:32:54
    PDT 2011 -->

Модель Java — сопоставлена ​​с XML-схемой Yahoo через метаданные XML

Поскольку мы не можем предоставить второй набор сопоставлений объектной модели с помощью аннотаций, мы должны предоставить последующие сопоставления, используя метаданные MOXy XML. По умолчанию документ отображения MOXy используется для дополнения любых аннотаций, указанных в модели. Однако, если установлен флаг xml-mapping-metadata-complete, то метаданные XML полностью заменят метаданные, предоставленные аннотациями (аннотации для отображения Google останутся в POJO, но флаг xml-mapping-metadata-complete говорит МОКСИ игнорировать их).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0"?>
<xml-bindings
    package-name="blog.weather"
    xml-mapping-metadata-complete="true">
    <xml-schema element-form-default="QUALIFIED">
        <xml-ns prefix="yweather" namespace-uri="http://xml.weather.yahoo.com/ns/rss/1.0"/>
    </xml-schema>
    <java-types>
        <java-type name="WeatherReport" xml-accessor-type="FIELD">
            <xml-root-element name="rss"/>
            <xml-type prop-order="location currentTemperature currentCondition forecast"/>
            <java-attributes>
                <xml-attribute java-attribute="location" xml-path="channel/yweather:location/@city"/>
                <xml-attribute java-attribute="currentTemperature" name="channel/item/yweather:condition/@temp"/>
                <xml-attribute java-attribute="currentCondition" name="channel/item/yweather:condition/@text"/>
                <xml-element java-attribute="forecast" name="channel/item/yweather:forecast"/>
            </java-attributes>
        </java-type>
        <java-type name="Forecast" xml-accessor-type="FIELD">
            <java-attributes>
                <xml-attribute java-attribute="dayOfTheWeek" name="day"/>
                <xml-attribute java-attribute="low"/>
                <xml-attribute java-attribute="high"/>
                <xml-attribute java-attribute="condition" name="text"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

демонстрация

В следующем демонстрационном коде будут прочитаны данные XML для службы погоды Yahoo и перенаправлены объекты обратно в XML. Из-за ошибки MOXy, связанной с несопоставленными разделами CDATA (https://bugs.eclipse.org/357145), для удаления его из XML-ввода использовался отфильтрованный XMLStreamReader:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package blog.weather;
  
import java.util.HashMap;
import java.util.Map;
  
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.StreamFilter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
  
import org.eclipse.persistence.jaxb.JAXBContextFactory;
  
public class YahooDemo {
  
    public static void main(String[] args) throws Exception {
        Map<String, Object> properties = new HashMap<String, Object>(1);
        properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "blog/weather/yahoo-binding.xml");
        JAXBContext jc = JAXBContext.newInstance(new Class[] {WeatherReport.class}, properties);
  
        XMLInputFactory xif = XMLInputFactory.newFactory();
        StreamSource xml = new StreamSource("http://weather.yahooapis.com/forecastrss?w=3369");
        XMLStreamReader xsr = xif.createXMLStreamReader(xml);
        xsr = xif.createFilteredReader(xsr, new CDATAFilter());
  
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        WeatherReport weatherReport = (WeatherReport) unmarshaller.unmarshal(xsr);
  
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(weatherReport, System.out);
    }
  
    private static class CDATAFilter implements StreamFilter {
  
        public boolean accept(XMLStreamReader xsr) {
            return XMLStreamReader.CDATA != xsr.getEventType();
        }
  
    }
  
}

Выход

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

01
02
03
04
05
06
07
08
09
10
<?xml version="1.0" encoding="UTF-8"?>
   <channel>
      <yweather:location city="Ottawa"/>
      <item>
         <yweather:forecast day="Thu" low="57" high="74" text="Partly Cloudy"/>
         <yweather:forecast day="Fri" low="53" high="79" text="Partly Cloudy"/>
      </item>
   </channel>
</rss>

Ссылка: сопоставление объектов с несколькими XML-схемами — пример погоды от нашего партнера по JCG Блейза Дафана из блога Java XML & JSON Binding .

Статьи по Теме :