#1378 Updating xml "in place"

tcolar Tue 11 Jan 2011

I'd like to update a value in an xml document. Ideally I wanted to do it "in place" but that does not seem possible.

So I read the file in memory:

usersXml := folder + `conf/users.xml`
Str uXml := usersXml.readAllStr
usersRoot := XParser(uXml.in).parseDoc(true).root

Then addd ann element

XElem user := XElem("user")
user.addAttr("username", "$userName")
usersRoot.add(user)

Then I try to write the file

usersRoot.write(Env.cur.out)
usersRoot.write(usersXml.out)
usersXml.out.close

The statement:

usersRoot.write(Env.cur.out)

does print out the xml code as expected to stdout (for testing).

But the XML file itself always end up being wiped-out (empty).

What am I missing ?

vkuzkokov Tue 11 Jan 2011

abstract OutStream out(Bool append := false, Int? bufferSize := (Int?)4096)

Open a new buffered OutStream used to write to this file.

i.e.

usersRoot.write(usersXml.out)
usersXml.out.close

opens two distinct OutStreams.

out := usersXml.out
usersRoot.write(out)
out.close

will do the trick.

tcolar Tue 11 Jan 2011

Ho yeah I keep forgetting about this :)

tcolar Tue 11 Jan 2011

I still find myself confused often by the "overlap" between file.open and file.out methods.

Also, say I want to open a file an add ** at the end of each line (example), I tried using a buffer like this:

buf := Buf()
myFile.eachLine |Str line|
{
  echo(line)
  buf.printLine("$line **")
}
// write the updated file
myFile.out.writeBuf(buf).close

It does print all the lines in the file ... however the file itself stays unmodified ... why ??

I also tried with:

myFile.open.writeBuf(buf).close

That did not work either ... also what is the difference between myFile.out.writeBuf(buf).close and myFile.open.writeBuf(buf).close ??

tcolar Tue 11 Jan 2011

It seem every long while, when I try to use those File open/out methods I need to fiddle around to get it right ... so maybe it's not intuitive or maybe I'm just not understanding it right.

file.open...
file.out...
file.open.out....

When should I use which / why ?

brian Tue 11 Jan 2011

@tcolar,

There are three ways to open a file:

  1. stream based using in or out
  2. random access using open
  3. memory map using mmap

In general unless you want to seek to explicit positions you shouldn't use open. Just use streaming APIs - it is most efficient. Unlike Java though if you open for random access, you can still treat it as a stream (all random access bytes are represented as Buf with stream access).

In your case the problem with your code is that you aren't flipping the buffer, so there is nothing to read back out from it. Consider this code:

Env.cur.out.printLine("With no flip:")
Env.cur.out.writeBuf( Buf().printLine("hello 1") )
Env.cur.out.printLine("With flip:")
Env.cur.out.writeBuf( Buf().printLine("hello 2").flip )

A buffer maintains a cursor for read/writes. As you write the cursor is at the end. So if you try and read from the buffer there is nothing to drain. If you call Buf.flip, it resets the cursor to the start so that everything you wrote is ready to read. I believe NIO works like this too.

tcolar Tue 11 Jan 2011

I see.

I assumed that writeBuf would have wrote the whole buffer.

tcolar Tue 11 Jan 2011

One more question about updating XML - Any way to keep the comments ??

I do:

usersXml := File(`/tmp/test.xml`)
usersRoot := XParser(uXml.in).parseDoc(true).root
// add an element to the xml root
usersRoot.add(XElem("user"))
// then save the doc
out := usersXml.out
usersRoot.doc.write(out)
out.close

While it works well (the doc gets updated) .... it does loose all of the (useful) comments in the XML ... anyway to keep those ?

Thanks.

brian Tue 11 Jan 2011

I assumed that writeBuf would have wrote the whole buffer.

Here is a simple way to remember how it works. In Java the convention for writing byte arrays is always this:

write(byte[] bytes, int off, int len)

In Fantom, the offset and length are part of the Buf itself. The offset is Buf.pos, and the length is Buf.remaining. You can control these values with seek, flip, etc.

One more question about updating XML - Any way to keep the comments ??

There is nothing in the current parser to keep comments or whitespace.

Login or Signup to reply.