javaxpathplistapache-config

Setting/adding property in array using XMLPropertyListConfiguration with XPathExpressionEngine


I'm trying to add/update a property in a .plist file using an XPath key specification. I've tested with a file that looks like this:

<?xml version="1.0"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="1.0">
  <dict>
    <key>testProp</key>
    <array>
      <string>text</string>
      <string>54</string>
      <string>2023-11-09T16:29:34Z</string>
    </array>
    <key>objectProp</key>
    <array>
      <string>text</string>
      <integer>54</integer>
      <date>2023-11-09T16:29:34Z</date>
    </array>
  </dict>
</plist>

and an XMLPropertyListConfiguration object built like this:

File file = new File("path/to/config.plist");
XMLPropertyListConfiguration configuration = 
  new FileBasedConfigurationBuilder<>(XMLPropertyListConfiguration.class)
    .configure(new Parameters().xml()
      .setFile(file)
      .setExpressionEngine(new XPathExpressionEngine()))
    .getConfiguration();

// do any changes, then save the file using:
FileHandler handler = new FileHandler(configuration);
handler.save(file);

My goal: edit a value in one of the array properties, or insert a new value into the array. I had thought that configuration.setProperty("/testProp[2]", "a different string") would result in the following (some parts cut for brevity):

<key>testProp</key>
<array>   
  <string>text</string>
  <string>a different string</string>
  <string>2023-11-09T16:29:34Z</string>
</array>

but instead it added testProp[2] as its own property:

<?xml version="1.0"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="1.0">
  <dict>
    <key>testProp</key>
    <array>
      <string>text</string>
      <string>54</string>
      <string>2023-11-09T16:29:34Z</string>
    </array>
    <key>objectProp</key>
    <array>
      <string>text</string>
      <integer>54</integer>
      <date>2023-11-09T16:29:34Z</date>
    </array>
    <key>testProp[2]</key>
    <string>a different string</string>
  </dict>
</plist>

It's my understanding that you use the testProp[n] predicate to specify the nth property/value of testProp, but clearly that's not the case here. Is there something I'm missing about arrays specifically? Or would I have to read testProp as a List, modify the list, and then save the whole list back as testProp? The best solution I'm looking for is something that can include all relevant information in the XPath string, but if I need to handle the index in the array separately that can be handled too.


Solution

  • As far as I can tell, you cannot use standard XPath expressions to access plist properties items. configuration.getProperty("/testProp"); returns an ArrayList<String> with the value [text, 54, 2023-11-09T16:29:34Z]. I tried various XPath expressions to access list indices(e.g./testProp[1][1], /testProp[1]/child:1), but none of them worked. Therefore, you might need to modify the ArrayList directly and set testProp as a whole.

    File file = new File("path/to/config.plist");
    XMLPropertyListConfiguration configuration = 
      new FileBasedConfigurationBuilder<>(XMLPropertyListConfiguration.class)
        .configure(new Parameters().xml()
          .setFile(file)
          .setExpressionEngine(new XPathExpressionEngine()))
        .getConfiguration();
    
    // Java 17 grammar
    var testProp = configuration.getProperty("/testProp");
    if (testProp instance of ArrayList testPropList) {
        testPropList.set(1, "a different string"); // change the 2nd string to 'a different string'
    }
    configuration.setProperty("testProp", testProp); // set the whole 'testProp'
    FileHandler handler = new FileHandler(configuration);
    handler.save(file);