Перейти к основному содержанию
`Ctrl`+`K`

# Работа с серализированными данными

# JSON

## JObject

Представляет собой обертку над мапой ключ-значение. Отлично подходит для небольших объектов ключ-значение и для больших объектов без конкретной структуры.

### Способы получения

    val fromStr = "".ns.toJValue.asJObject
    val fromRop = JEmbeddedDoc.parseProperty(rop, "Имя json контейнера").asJObject

### Геттеры

- Для получения данных из контейнера по ключу используйте один из типизированных геттеров `ru.bitec.app.gtk.lang.json.JObjectAbs`:

      val jObj = JEmbeddedDoc.parseProperty(rop,"Имя json контейнера").asJObject
      jObj.getNString(key)
      jObj.getBoolean(key)
      jObj.getNLong(key)
      jObj.getNDate(key)
      jObj.getNGid(key)
      jObj.childJObject(key)
      jObj.childJArray(key)

      /** Общий гетер
      * ВАЖНО: JArray и JObject полученные через этот метод являются копиями т.е. изменеия в них не отобразятся в ориг. объекте
      */
      jObj.getJValue(key: NString)

- Для записи в контейнер используйте типизированные сеттеры `ru.bitec.app.gtk.lang.json.JObjectAbs`:

        val jObj = "{}".ns.toJValue.asJObject
        jObj.set("key".ns, value)
        val innerJArray = jObj.createJArray("array".ns)
        val innerJObject = jObj.createJObject("object".ns)

## Object Mapper

Конвертирует JSON строку в конкретные объекты.

### Cериализация и десериализация

            val jsonString = """{"key":{"id":10,"value":"someValue"}}"""
            /** ВАЖНО все классы используемые для десериализации должны
             * быть объявлены вне других классов
             */
            case class Example(id: NLong, value: NString)

            val map = GtkObjectMapper().readValue[Map[NString, Example]](jsonString)

            val newJsonString = GtkObjectMapper().writeValueAsString(map)

### Cериализация и десериализация абстрактных типов

      // При сериализации наследников им добавится ключ "type"
    @JsonTypeInfo(
      use = JsonTypeInfo.Id.NAME,
      include = As.PROPERTY,
      property = "type")
    @JsonSubTypes(
      value = Array(
        new JsonSubTypes.Type(value = classOf[Type1], name = "type1"),
        new JsonSubTypes.Type(value = classOf[Type2], name = "type2")
      )
    )
    trait Base {
      def id: NLong

      def sValue: NString
    }

    @JsonTypeName("type1")
    case class Type1(id: NLong, sValue: NString, nValue: NNumber) extends Base

    @JsonTypeName("type2")
    case class Type2(id: NLong, sValue: NString, gidValue: NGid) extends Base

    val sJSON ="""{
      "id": 10,
      "sValue": "Hello world",
      "type": "type2",
      "gidValue": "0/0"
    }"""

    val result  = GtkObjectMapper().readValue[Base](sJSON) match {
      case Type1(id, sValue, nValue) => nValue.toString()
      case Type2(id, sValue, gidValue) => gidValue.toString
    }

# XML

## scala.XML

### Сериализация и десериализация

    // Получить их строки
    val xmlText = """*XML строка*"""
    val xml = XML.loadString(xmlText)

    // задать напрямую в коде, в '{}' можно указывать выражения которые возвращают текст или элементы
    val xml = <Node>
      {Seq(1,2,3).map(num => <Node>{num}</Node>)}
    </Node>

    // задать напрямую в коде (scala 3)
    val xml = xml"""<Node></Node>"""

    // Превратить обратно в строку
    val xmlText = xml.toString

### Получение значений

        val xml =
      <example>
        <tag name="Name"/>
        <children>
          <child name="1"/>
          <child name="2"/>
          <child name="3"/>
          <child name="3">Hello</child>
        </children>
      </example>

    // собирает все ноды в "example" c тегом "children", потом собирает все ноды в "children" c тегом "child"
    val children = xml \ "children" \ "child"
    // собирает все ноды "child" во всем документе
    val children2 = xml \\ "child"

    // '\@' берет атрибут у элемента и возвращает его строковое значение
    // ВАЖНО если элементов несколько то сложит их значения в одну строку, используйте если уверены что работаете с 1 элементом
    val firstChildrenName = children.headOption.toSeq \@ "name"

    // если нужно просто получить значения в виде списка то используйте '\' и добавте "@" к названию атрибута
    val childrenNames = (children \ "@name").map(_.text)

### Cериализация и десериализация в объекты

Автоматической конвертации в объекты в библиотеке нет, и лучший вариант писать самому:

    case class Child(sName: NString, sText: NString) {
      def toXML(): Node = {
        // Если в значение атрибута передать null, то атрибут будет удален из элемента
        <child name={sName.get}>
          {sText.get}
        </child>
          .copy(minimizeEmpty = true) // складывает Node типа <child></child> в <child/>
      }
    }

    object Child {
      def fromXML(node: Node): Child = {
        Child(
          /*
          *  Осторожнее с .text
          *  1) Он складывает все текстовые ноды в одну строку
          *  2) '\' и '\\' возвращают пустые NodeSeq если ничего не нашли,
          *   и .text вернет пустую строку, что не всегда желательно
          */
          sName = (node \ "@name").text.nullif(""),
          sText = node.text.nullif("")
        )
      }
    }

    // <child name="Joe">Hello world</child>
    val exampleTo1 = Child(sName = "Joe", "Hello world").toXML()
    // <child/>
    val exampleTo2 = Child(sName = None.ns, None.ns).toXML()

### Обработка XML

        val xml =
      <Отчет>
        <Документы>
          <Документ имя="Дог №1">
            <Сумма тип="Поступление">5000</Сумма>
            <Сумма тип="Расходы">100</Сумма>
          </Документ>
          <Документ имя="Дог №4">
            <Сумма тип="Поступление">200</Сумма>
            <Сумма тип="Расходы">30</Сумма>
          </Документ>
          <Документ имя="Дог №2">
            <Сумма тип="Расходы">100</Сумма>
          </Документ>
        </Документы>
      </Отчет>

    // Правило которое для элемента "Сумма" c типом "Поступление" разделит сумму указанную в нем на 2
    object subtractTAX extends RewriteRule {
      override def transform(n: Node): Seq[Node] = n match {
        case elem: Elem if elem.label == "Сумма" && (elem \ "@тип").text == "Поступление" =>
          val nCurrSum = NNumber.fromAny(elem.text)
          val nSumAfterTAX = (nCurrSum * 50.nn / 100.nn).round(2)
          elem.copy(child = Text(nSumAfterTAX.toString))
        case rest => rest
      }
    }

    // Правило которое для элемента "Документ" посчитает сумму указанную в элементах "Сумма"
    object finalDocSum extends RewriteRule {
      override def transform(n: Node): Seq[Node] = n match {
        case elem: Elem if elem.label == "Документ" =>
          val sum = elem.descendant.collect {
            case elem: Elem if elem.label == "Сумма" && (elem \ "@тип").text == "Поступление" =>
              NNumber.fromAny(elem.text).nvl(0.nn)
          }.fold(0.nn)(_ + _)

          elem % Attribute(None, "сумма", Text(sum.toString), Null)
        case rest => rest
      }
    }

    // Правило которое для элемента "Отчет" посчитает сумму указанную в атрибутах для элементов "Документ"
    object finalReportSum extends RewriteRule {
      override def transform(n: Node): Seq[Node] = n match {
        case elem: Elem if elem.label == "Отчет" =>
          val nFinalSum = (elem \\ "Документ")
                  .map(n => (n \ "@сумма").text)
                  .map(sSum => NNumber.fromAny(sSum).nvl(0.nn))
                  .fold(0.nn)(_ + _)

          elem % Attribute(None, "сумма", Text(nFinalSum.toString), Null)
        case rest => rest
      }
    }

    // собираем общее правило в порядке в котором мы хотим чтобы документ обработался
    val ruleTransformer = new RuleTransformer(subtractTAX, finalDocSum, finalReportSum)

    /*
    <Отчет сумма="2600">
    <Документы>
    <Документ сумма="2500" имя="Дог №1">
      <Сумма тип="Поступление">2500</Сумма>
      <Сумма тип="Расходы">100</Сумма>
    </Документ>
    <Документ сумма="100" имя="Дог №4">
      <Сумма тип="Поступление">100</Сумма>
      <Сумма тип="Расходы">30</Сумма>
    </Документ>
    <Документ сумма="0" имя="Дог №2">
      <Сумма тип="Расходы">100</Сумма>
    </Документ>
    </Документы>
    </Отчет>
    */
    val xmlResult = ruleTransformer(xml)