1

I'm using VTD-XML to update XML files. In this I am trying to get a flexible way of maintaining attributes on an element. So if my original element is:

<MyElement name="myName" existattr="orig" />

I'd like to be able to update it to this:

<MyElement name="myName" existattr="new" newattr="newValue" />

I'm using a Map to manage the attribute/value pairs in my code and when I update the XML I'm doing something like the following:

private XMLModifier xm = new XMLModifier();
xm.bind(vn);

for (String key : attr.keySet()) {
     int i = vn.getAttrVal(key);

     if (i!=-1) {
         xm.updateToken(i, attr.get(key));
     } else {
         xm.insertAttribute(key+"='"+attr.get(key)+"'");
     }
}
vn = xm.outputAndReparse();

This works for updating existing attributes, however when the attribute doesn't already exist, it hits the insert (insertAttribute) and I get "ModifyException"

com.ximpleware.ModifyException: There can be only one insert per offset
at com.ximpleware.XMLModifier.insertBytesAt(XMLModifier.java:341)
at com.ximpleware.XMLModifier.insertAttribute(XMLModifier.java:1833)

My guess is that as I'm not manipulating the offset directly this might be expected. However I can see no function to insert an an attribute at a position in the element (at end).

My suspicion is that I will need to do it at the "offset" level using something like xm.insertBytesAt(int offset, byte[] content) - as this is an area I have needed to get into yet is there a way to calculate the offset at which I can insert (just before the end of the tag)?

Of course I may be mis-using VTD in some way here - if there is a better way of achieving this then happy to be directed.

Thanks

Dazed
  • 1,069
  • 9
  • 34

1 Answers1

2

That's an interesting limitation of the API I hadn't encountered yet. It would be great if vtd-xml-author could elaborate on technical details and why this limitation exists.

As a solution to your problem, a simple approach would be to accumulate your key-value pairs to be inserted as a String, and then to insert them in a single call after your for loop has terminated.

I've tested that this works as per your code:

private XMLModifier xm_ = new XMLModifier();
xm.bind(vn);

String insertedAttributes = "";
for (String key : attr.keySet()) {
     int i = vn.getAttrVal(key);

     if (i!=-1) {
         xm.updateToken(i, attr.get(key));
     } else {
         // Store the key-values to be inserted as attributes
         insertedAttributes += " " + key + "='" + attr.get(key) + "'";
     }
}
if (!insertedAttributes.equals("")) {
   // Insert attributes only once
   xm.insertAttribute(insertedAttributes);
}

This will also work if you need to update the attributes of multiple elements, simply nest the above code in while(autoPilot.evalXPath() != -1) and be sure to set insertedAttributes = ""; at the end of each while loop.

Hope this helps.

xlm
  • 6,854
  • 14
  • 53
  • 55
  • @XLM, thanks for the suggestion. I no longer get the ModifyException - however as I am immediately issue a parse "vn = xm.outputAndReparse();" I now get "ParseException: Error in attr: Attr name not unique". – Dazed Jul 16 '13 at 08:37
  • @vtd-xml-author, xlm - I confess I don't know if this approach I'm taking (reparse) is correct, but saw comments elsewhere that suggested that it was needed to be able to use the VTDNav after Modifying. Is there anyway to print out what the XMLModifier contains before reparse? – Dazed Jul 16 '13 at 08:47
  • Ignore my last comments - I've got it working - turned out I wasn't actually doing the "xm.bind" in my real code quite where I showed it in my stripped down code. Thanks for your help. – Dazed Jul 16 '13 at 09:26
  • Awesome, glad you got it working. I've used my VTDNav for querying and making additional modifications so I would be curious to understand if/why you couldn't do so. – xlm Jul 16 '13 at 12:46
  • @xlm - are you indicating that I shouldn't need to do the outputAndReparse() after updating/inserting attributes - to make the VTDNav contain latest position? – Dazed Jul 17 '13 at 09:45
  • @Dazed For my use case I don't output until I'm done making all my queries and modifications and VTD-XML works great, but I'm not making subsequent queries based on those modifications - I'm not exactly sure what will happen if I do. Why don't you try it and let me know. – xlm Jul 17 '13 at 23:56