iniziare bene con questo:
def addChild(n: Node, newChild: Node) = n match {
case Elem(prefix, label, attribs, scope, child @ _*) =>
Elem(prefix, label, attribs, scope, child ++ newChild : _*)
case _ => error("Can only add children to elements!")
}
Il metodo ++
funziona qui perché è un child
Seq[Node]
, e newChild
è un Node
, che si estende NodeSeq
, che si estende Seq[Node]
.
Ora, questo non cambia nulla, perché XML in Scala è immutabile. Produrrà un nuovo nodo, con le modifiche richieste. L'unico costo è quello di creare un nuovo oggetto Elem
, oltre a creare un nuovo Seq
di bambini. I nodi figli, essi stessi, non sono copiati, solo per riferimento, il che non causa problemi perché sono immutabili.
Tuttavia, se si aggiungono i figli a un nodo verso il basso nella gerarchia XML, le cose si complicano. Un modo sarebbe utilizzare le cerniere, come descritto in this blog.
È tuttavia possibile utilizzare scala.xml.transform
, con una regola che modificherà un nodo specifico per aggiungere il nuovo figlio. In primo luogo, scrivere una nuova classe trasformatore:
class AddChildrenTo(label: String, newChild: Node) extends RewriteRule {
override def transform(n: Node) = n match {
case n @ Elem(_, `label`, _, _, _*) => addChild(n, newChild)
case other => other
}
}
Quindi, utilizzare in questo modo:
val newXML = new RuleTransformer(new AddChildrenTo(parentName, newChild)).transform(oldXML).head
Su Scala 2.7, sostituire head
con first
.
Esempio su Scala 2.7:
scala> val oldXML = <root><parent/></root>
oldXML: scala.xml.Elem = <root><parent></parent></root>
scala> val parentName = "parent"
parentName: java.lang.String = parent
scala> val newChild = <child/>
newChild: scala.xml.Elem = <child></child>
scala> val newXML = new RuleTransformer(new AddChildrenTo(parentName, newChild)).transform(oldXML).first
newXML: scala.xml.Node = <root><parent><child></child></parent></root>
si potrebbe rendere più complessa per ottenere l'elemento giusto, se solo il genitore non è sufficiente. Tuttavia, se è necessario aggiungere il figlio a un genitore con un nome comune di un indice specifico, probabilmente è necessario seguire la procedura delle cerniere.
Ad esempio, se si dispone di <books><book/><book/></books>
e si desidera aggiungere <author/>
al secondo, ciò sarebbe difficile da fare con il trasformatore di regole. Avresti bisogno di un RewriteRule contro books
, che sarebbe poi ottenere il suo child
(che in realtà avrebbe dovuto essere chiamato children
), trovare il n ° book
in loro, aggiungere il nuovo bambino a quello, e poi ricomporre i bambini e costruire il nuovo nodo. È fattibile, ma le cerniere potrebbero essere più facili se devi farlo troppo.
Vedo che non hai ha accettato una risposta ancora. Potresti voler fare un passo indietro e vedere se hai persino bisogno di usare il trasformatore di regole o la cerniera o anche "aggiungere un bambino a una nota". Può dipendere dal fatto se il documento XML di destinazione rispecchia sostanzialmente i dati lineari che contengono alcune relazioni genitore/figlio e quanto è grande. Ad esempio, è possibile mantenere un buffer di oggetto e alla fine eseguire un passaggio per convertire il buffer di oggetti in un documento xml. Oppure puoi prima creare l'xml 'children' e fare' {children} 'quando l'elaborazione genitore è terminata. –
huynhjl
@huynhjl Grazie per il consiglio. Devo tradurre un foglio di calcolo in xml. In questo modo esclude Rule Transformer mentre opera sulla mappatura da XML a XML. Il foglio di calcolo ha le informazioni sul nome del tag, ma è tutto lineare, quindi ho bisogno di reinserire le informazioni di nidificazione. Il modo in cui lo farei di solito è usando una pila e aggiungendola quando so che il tag corrente è un bambino e si apre quando so che il nodo corrente è completo. Ecco perché volevo aggiungere bambini a un nodo anonimo. –