Scala XML Building: Adding children to existing Nodes

前端 未结 9 1228
感动是毒
感动是毒 2020-12-15 04:42

I Have an XML Node that I want to add children to over time:

val root: Node = 

But I cannot see methods such as

相关标签:
9条回答
  • 2020-12-15 05:35

    In Scala xml nodes are immutable, but can do this:

    var root = <model/>
    
    def addToModel(child:Node) = {
      root = root match {
        case <model>{children@ _*}</model> => <model>{children ++ child}</model>
        case other => other
      }
    }
    
    addToModel(<subsection>content</subsection>)
    

    It rewrites a new xml, by making a copy of the old one and adding your node as a child.

    Edit: Brian provided more info and I figured a different to match.

    To add a child to an arbitrary node in 2.8 you can do:

    def add(n:Node,c:Node):Node = n match { case e:Elem => e.copy(child=e.child++c) }
    

    That will return a new copy of parent node with the child added. Assuming you've stacked your children nodes as they became available:

    scala> val stack = new Stack[Node]()
    stack: scala.collection.mutable.Stack[scala.xml.Node] = Stack()
    

    Once you've figured you're done with retrieving children, you can make a call on the parent to add all children in the stack like this:

    stack.foldRight(<parent/>:Node){(c:Node,n:Node) => add(n,c)}
    

    I have no idea about the performance implication of using Stack and foldRight so depending on how many children you've stacked, you may have to tinker... Then you may need to call stack.clear too. Hopefully this takes care of the immutable nature of Node but also your process as you go need.

    0 讨论(0)
  • 2020-12-15 05:37

    your root definition is actually an Elem object, a subclass of node, so if you drop your unnecessary Node typing (which hides its implementation) you could actually do a ++ on it since the Elem class has this method.

    val root = <model/>
    val myChild = <myChild/>
    root.copy(child = root.child ++ myChild)
    

    scala ev:

    root: scala.xml.Elem = <model/>
    myChild: scala.xml.Elem = <mychild/>
    res2: scala.xml.Elem = <model><mychild/></model>
    

    Since every Elem and every Node is a NodeSeq you can add these pretty effectively even if what you are appending is an unknown sequence:

    val root = <model/>
    //some node sequence of unknown subtype or structure
    val children: scala.xml.NodeSeq = <node1><node2/></node1><node3/> 
    root.copy(child = root.child ++ children)
    

    scala ev:

    root: scala.xml.Elem = <model/>
    children: scala.xml.NodeSeq = NodeSeq(<node1><node2/></node1>, <node3/>)
    res6: scala.xml.Elem = <model><node1><node2/></node1><node3/></model>
    
    0 讨论(0)
  • 2020-12-15 05:38

    I agree that you have to work with XML "the other way around". Keep in mind you don't have to have the entire XML document available when information becomes available, you only need to compose the XML when the application needs to read it.

    Keep your subsection state however you want to, when you need the XML, wrap it all together.

      val subsections : List[Elem]
    
      def wrapInModel(f : => Elem) = {
        <model>{f}</model>
      }
    
      wrapInModel(subsections)
    

    or

      def wrapInModel(f : => Elem) = {
        <model>{f}</model>
      }
      wrapInModel(<subsection>content</subsection>)
    
    0 讨论(0)
提交回复
热议问题