Amantuer Rewuhan
Amantuer Rewuhan

Reputation: 35

How to convert map to node in Scala?

I have a Map[String,List[String]] type data and want to save as a xml file in Scala. If I choose to use scala.xml.XML.save method, I need to convert map to node.

But I didn't find a way to do it. And it seems there is no method in map or node library could do it.

Upvotes: 0

Views: 255

Answers (2)

Ivan Kurchenko
Ivan Kurchenko

Reputation: 4063

You can try also next library : https://github.com/mthaler/xmlconfect - the idea similar to other codec libraries (e.g. circe, Play Json etc.) - it build codec based on types via implicit's. In your case it would look like next:

import scala.xml.PrettyPrinter
import com.mthaler.xmlconfect._
import com.mthaler.xmlconfect.ProductFormatInstances._
import com.mthaler.xmlconfect.BasicTextFormats._
import com.mthaler.xmlconfect.CollectionFormats._

object XmlFormat {
  type RawData = Map[String, List[String]]

  case class TypedDataElement(item: String)

  object TypedDataElement {
    implicit val format: XmlElemFormat[TypedDataElement] = xmlFormat1(TypedDataElement.apply)
  }

  case class TypedDataNode(key: String, item: List[TypedDataElement])

  object TypedDataNode {
    implicit val format: XmlElemFormat[TypedDataNode] = xmlFormat2(TypedDataNode.apply)
  }

  case class AllTypedData(data: List[TypedDataNode]) {
    def toRawData: RawData = {
      data.map(item => item.key -> item.item.map(_.item)).toMap
    }
  }

  object AllTypedData {
    implicit val format: XmlElemFormat[AllTypedData] = xmlFormat1(AllTypedData.apply)

    def fromRaw(raw: RawData): AllTypedData = {
      val all = raw.toList.map {
        case (key, values) =>  TypedDataNode(key, values.map(TypedDataElement.apply))
      }
      AllTypedData(all)
    }
  }

  def main(args: Array[String]): Unit = {
    val rawData: RawData = Map("node" -> List("value1", "value2"))
    val typedData = AllTypedData.fromRaw(rawData)
    val printer = new PrettyPrinter(80, 4)
    println(printer.formatNodes(typedData.toNode))
  }
}

Case classes was introduced in order to provide key names.

The result output in this particular case would be:

<AllTypedData>
    <TypedDataNode>
        node
        <TypedDataElement>value1</TypedDataElement>
        <TypedDataElement>value2</TypedDataElement>
    </TypedDataNode>
</AllTypedData>

Upvotes: 0

chipz
chipz

Reputation: 796

You have to create the converter manually since xml can be pretty different one another, this is an example you can use.

import scala.xml.{Node, NodeSeq}

val a:Map[String, List[String]] = Map("animal" -> List("cat", "dog", "bird"), "fruit" -> List("banana", "apple"))

def convertToXML(parametersToCreateXML: Map[String,List[String]]): NodeSeq = {
  def generateTitleToData(keyValue: (String, List[String])): Node = {
    <member>
      <name>{keyValue._1}</name>
      <values>
        {keyValue._2.map(x =>generateData(x))}
      </values>
    </member>
  }

  def generateData(value: String): Node = {
    <value>
      <string>{value}</string>
    </value>
  }

  parametersToCreateXML.map(x => generateTitleToData(x)).toSeq
}

convertToXML(a)

res0: scala.xml.NodeSeq =
NodeSeq(<member>
      <name>animal</name>
      <values>
        <value>
      <string>cat</string>
    </value><value>
      <string>dog</string>
    </value><value>
      <string>bird</string>
    </value>
      </values>
    </member>, <member>
      <name>fruit</name>
      <values>
        <value>
      <string>banana</string>
    </value><value>
      <string>apple</string>
    </value>
      </values>
    </member>)

as you can see, the conversion from Seq[Node] to NodeSeq is done implicitly.

Upvotes: 1

Related Questions