Refactor XML parsing in rest (#94268)

* Refactor XML parsing in rest

* Adjust caplog check

* Adjust

* Rename

* Simplify
This commit is contained in:
epenet 2023-06-15 09:15:25 +02:00 committed by GitHub
parent 61d260e5fe
commit 580b09d0f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 35 additions and 25 deletions

View File

@ -26,3 +26,10 @@ REST = "rest"
REST_DATA = "rest_data"
METHODS = ["POST", "GET"]
XML_MIME_TYPES = (
"application/rss+xml",
"application/xhtml+xml",
"application/xml",
"text/xml",
)

View File

@ -3,14 +3,19 @@ from __future__ import annotations
import logging
import ssl
from xml.parsers.expat import ExpatError
import httpx
import xmltodict
from homeassistant.core import HomeAssistant
from homeassistant.helpers import template
from homeassistant.helpers.httpx_client import create_async_httpx_client
from homeassistant.helpers.json import json_dumps
from homeassistant.util.ssl import SSLCipherList
from .const import XML_MIME_TYPES
DEFAULT_TIMEOUT = 10
_LOGGER = logging.getLogger(__name__)
@ -59,6 +64,26 @@ class RestData:
"""Set 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:
"""Get the latest data from REST service with provided method."""
if not self._async_client:

View File

@ -3,11 +3,9 @@ from __future__ import annotations
import logging
import ssl
from xml.parsers.expat import ExpatError
from jsonpath import jsonpath
import voluptuous as vol
import xmltodict
from homeassistant.components.sensor import (
DOMAIN as SENSOR_DOMAIN,
@ -26,7 +24,6 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.json import json_dumps
from homeassistant.helpers.template_entity import TemplateSensor
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
@ -127,26 +124,7 @@ class RestSensor(RestEntity, TemplateSensor):
def _update_from_rest_data(self) -> None:
"""Update state from the rest data."""
value = self.rest.data
_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)
value = self.rest.data_without_xml()
if self._json_attrs:
self._attr_extra_state_attributes = {}

View File

@ -899,7 +899,7 @@ async def test_update_with_xml_convert_bad_xml(
state = hass.states.get("sensor.foo")
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
@ -936,7 +936,7 @@ async def test_update_with_failed_get(
state = hass.states.get("sensor.foo")
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