У меня есть многострочный файл JSON с записями, содержащими специальные символы, закодированные как шестнадцатеричные числа. Вот пример одной записи JSON:
{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}
Эта запись должна быть {"value":"ıarines Bintıç Ramuçlar"}
, например. Символ '"' заменяется соответствующим шестнадцатеричным символом \x22, а другие специальные символы Unicode заменяются одним или двумя шестнадцатеричными символами (например, \xC3\xA7 кодирует ç и т. д.)
Мне нужно преобразовать похожие строки в обычную строку Unicode в Scala, чтобы при печати она выдавала {"value":"ıarines Bintıç Ramuçlar"}
без шестнадцатеричных знаков.
В Python я могу легко расшифровать эти записи с помощью строки кода:
>>> a = "{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"
>>> a.decode("utf-8")
u'{"value":"\u0131arines Bint\u0131\xe7 Ramu\xe7lar"}'
>>> print a.decode("utf-8")
{"value":"ıarines Bintıç Ramuçlar"}
Но в Scala я не могу найти способ его декодировать. Я безуспешно пытался преобразовать его следующим образом:
scala> val a = """{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"""
scala> print(new String(a.getBytes(), "UTF-8"))
{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}
Я также попробовал URLDecoder, который нашел в решении аналогичной проблемы (но с URL):
scala> val a = """{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"""
scala> print(java.net.URLDecoder.decode(a.replace("\\x", "%"), "UTF-8"))
{"value":"ıarines Bintıç Ramuçlar"}
Это дало желаемый результат для этого примера, но кажется небезопасным для общих текстовых полей, поскольку он предназначен для работы с URL-адресами и требует замены всех \x
на %
в строке.
Есть ли в Scala лучший способ решить эту проблему?
Я новичок в Scala и буду благодарен за любую помощь.
ОБНОВЛЕНИЕ: я создал собственное решение с помощью javax.xml.bind.DatatypeConverter.parseHexBinary
. Пока работает, но выглядит громоздко и совсем не изящно. Я думаю, что должен быть более простой способ сделать это.
Вот код:
import javax.xml.bind.DatatypeConverter
import scala.annotation.tailrec
import scala.util.matching.Regex
def decodeHexChars(string: String): String = {
val regexHex: Regex = """\A\\[xX]([0-9a-fA-F]{1,2})(.*)""".r
def purgeBuffer(buffer: String, acc: List[Char]): List[Char] = {
if (buffer.isEmpty) acc
else new String(DatatypeConverter.parseHexBinary(buffer)).reverse.toList ::: acc
}
@tailrec
def traverse(s: String, acc: List[Char], buffer: String): String = s match {
case "" =>
val accUpdated = purgeBuffer(buffer, acc)
accUpdated.foldRight("")((str, b) => b + str)
case regexHex(chars, suffix) =>
traverse(suffix, acc, buffer + chars)
case _ =>
val accUpdated = purgeBuffer(buffer, acc)
traverse(s.tail, s.head :: accUpdated, "")
}
traverse(string, Nil, "")
}