mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Refactor XML parsing in rest (#94268)
* Refactor XML parsing in rest * Adjust caplog check * Adjust * Rename * Simplify
This commit is contained in:
parent
61d260e5fe
commit
580b09d0f2
@ -26,3 +26,10 @@ REST = "rest"
|
|||||||
REST_DATA = "rest_data"
|
REST_DATA = "rest_data"
|
||||||
|
|
||||||
METHODS = ["POST", "GET"]
|
METHODS = ["POST", "GET"]
|
||||||
|
|
||||||
|
XML_MIME_TYPES = (
|
||||||
|
"application/rss+xml",
|
||||||
|
"application/xhtml+xml",
|
||||||
|
"application/xml",
|
||||||
|
"text/xml",
|
||||||
|
)
|
||||||
|
@ -3,14 +3,19 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import ssl
|
import ssl
|
||||||
|
from xml.parsers.expat import ExpatError
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
import xmltodict
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import template
|
from homeassistant.helpers import template
|
||||||
from homeassistant.helpers.httpx_client import create_async_httpx_client
|
from homeassistant.helpers.httpx_client import create_async_httpx_client
|
||||||
|
from homeassistant.helpers.json import json_dumps
|
||||||
from homeassistant.util.ssl import SSLCipherList
|
from homeassistant.util.ssl import SSLCipherList
|
||||||
|
|
||||||
|
from .const import XML_MIME_TYPES
|
||||||
|
|
||||||
DEFAULT_TIMEOUT = 10
|
DEFAULT_TIMEOUT = 10
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -59,6 +64,26 @@ class RestData:
|
|||||||
"""Set url."""
|
"""Set url."""
|
||||||
self._resource = url
|
self._resource = url
|
||||||
|
|
||||||
|
def data_without_xml(self) -> str | None:
|
||||||
|
"""If the data is an XML string, convert it to a JSON string."""
|
||||||
|
_LOGGER.debug("Data fetched from resource: %s", self.data)
|
||||||
|
if (
|
||||||
|
(value := self.data) is not None
|
||||||
|
# If the http request failed, headers will be None
|
||||||
|
and (headers := self.headers) is not None
|
||||||
|
and (content_type := headers.get("content-type"))
|
||||||
|
and content_type.startswith(XML_MIME_TYPES)
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
value = json_dumps(xmltodict.parse(value))
|
||||||
|
except ExpatError:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"REST xml result could not be parsed and converted to JSON"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_LOGGER.debug("JSON converted from XML: %s", self.data)
|
||||||
|
return value
|
||||||
|
|
||||||
async def async_update(self, log_errors: bool = True) -> None:
|
async def async_update(self, log_errors: bool = True) -> None:
|
||||||
"""Get the latest data from REST service with provided method."""
|
"""Get the latest data from REST service with provided method."""
|
||||||
if not self._async_client:
|
if not self._async_client:
|
||||||
|
@ -3,11 +3,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import ssl
|
import ssl
|
||||||
from xml.parsers.expat import ExpatError
|
|
||||||
|
|
||||||
from jsonpath import jsonpath
|
from jsonpath import jsonpath
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
import xmltodict
|
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
DOMAIN as SENSOR_DOMAIN,
|
DOMAIN as SENSOR_DOMAIN,
|
||||||
@ -26,7 +24,6 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.exceptions import PlatformNotReady
|
from homeassistant.exceptions import PlatformNotReady
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.json import json_dumps
|
|
||||||
from homeassistant.helpers.template_entity import TemplateSensor
|
from homeassistant.helpers.template_entity import TemplateSensor
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
@ -127,26 +124,7 @@ class RestSensor(RestEntity, TemplateSensor):
|
|||||||
|
|
||||||
def _update_from_rest_data(self) -> None:
|
def _update_from_rest_data(self) -> None:
|
||||||
"""Update state from the rest data."""
|
"""Update state from the rest data."""
|
||||||
value = self.rest.data
|
value = self.rest.data_without_xml()
|
||||||
_LOGGER.debug("Data fetched from resource: %s", value)
|
|
||||||
if self.rest.headers is not None:
|
|
||||||
# If the http request failed, headers will be None
|
|
||||||
content_type = self.rest.headers.get("content-type")
|
|
||||||
|
|
||||||
if content_type and (
|
|
||||||
content_type.startswith("text/xml")
|
|
||||||
or content_type.startswith("application/xml")
|
|
||||||
or content_type.startswith("application/xhtml+xml")
|
|
||||||
or content_type.startswith("application/rss+xml")
|
|
||||||
):
|
|
||||||
try:
|
|
||||||
value = json_dumps(xmltodict.parse(value))
|
|
||||||
_LOGGER.debug("JSON converted from XML: %s", value)
|
|
||||||
except ExpatError:
|
|
||||||
_LOGGER.warning(
|
|
||||||
"REST xml result could not be parsed and converted to JSON"
|
|
||||||
)
|
|
||||||
_LOGGER.debug("Erroneous XML: %s", value)
|
|
||||||
|
|
||||||
if self._json_attrs:
|
if self._json_attrs:
|
||||||
self._attr_extra_state_attributes = {}
|
self._attr_extra_state_attributes = {}
|
||||||
|
@ -899,7 +899,7 @@ async def test_update_with_xml_convert_bad_xml(
|
|||||||
state = hass.states.get("sensor.foo")
|
state = hass.states.get("sensor.foo")
|
||||||
|
|
||||||
assert state.state == STATE_UNKNOWN
|
assert state.state == STATE_UNKNOWN
|
||||||
assert "Erroneous XML" in caplog.text
|
assert "REST xml result could not be parsed" in caplog.text
|
||||||
assert "Empty reply" in caplog.text
|
assert "Empty reply" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@ -936,7 +936,7 @@ async def test_update_with_failed_get(
|
|||||||
state = hass.states.get("sensor.foo")
|
state = hass.states.get("sensor.foo")
|
||||||
|
|
||||||
assert state.state == STATE_UNKNOWN
|
assert state.state == STATE_UNKNOWN
|
||||||
assert "Erroneous XML" in caplog.text
|
assert "REST xml result could not be parsed" in caplog.text
|
||||||
assert "Empty reply" in caplog.text
|
assert "Empty reply" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user