This is an automated email from the ASF dual-hosted git repository.

mcasters pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 1e12691081 Issues #1891 (XML cleanup of Properties Output transforms) 
(#6883)
1e12691081 is described below

commit 1e12691081992abe3de7ee09c9f638b7ad7e737f
Author: Matt Casters <[email protected]>
AuthorDate: Fri Mar 27 13:19:12 2026 +0100

    Issues #1891 (XML cleanup of Properties Output transforms) (#6883)
---
 .../transforms/propertyoutput/PropertyOutput.java  | 259 +++++-----
 .../propertyoutput/PropertyOutputData.java         |  11 +-
 .../propertyoutput/PropertyOutputDialog.java       | 198 +++-----
 .../propertyoutput/PropertyOutputMeta.java         | 426 +++++-----------
 .../messages/messages_en_US.properties             |   3 +-
 .../messages/messages_es_AR.properties             |   2 +-
 .../messages/messages_fr_FR.properties             |   2 +-
 .../messages/messages_it_IT.properties             |   2 +-
 .../messages/messages_ja_JP.properties             |   2 +-
 .../messages/messages_ko_KR.properties             |   2 +-
 .../messages/messages_pt_BR.properties             |   2 +-
 .../messages/messages_zh_CN.properties             |   2 +-
 .../propertyoutput/PropertyOutputIT.java           |  33 --
 .../propertyoutput/PropertyOutputMetaTest.java     |  78 +--
 .../test/resources/properties-output-legacy.xml    |  57 +++
 .../src/test/resources/properties-output.xml       |  37 ++
 .../src/test/resources/propertyOutput.hpl          | 549 ---------------------
 17 files changed, 451 insertions(+), 1214 deletions(-)

diff --git 
a/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutput.java
 
b/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutput.java
index 577182d5de..a962533920 100644
--- 
a/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutput.java
+++ 
b/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutput.java
@@ -34,12 +34,7 @@ import org.apache.hop.pipeline.transform.TransformMeta;
 
 /** Output rows to Properties file and create a file. */
 public class PropertyOutput extends BaseTransform<PropertyOutputMeta, 
PropertyOutputData> {
-
   private static final Class<?> PKG = PropertyOutputMeta.class;
-  public static final String CONST_PROPERTY_OUTPUT_LOG_ERROR_FINDING_FIELD =
-      "PropertyOutput.Log.ErrorFindingField";
-  public static final String 
CONST_PROPERTY_OUTPUT_LOG_CAN_NOT_CREATE_PARENT_FOLDER =
-      "PropertyOutput.Log.CanNotCreateParentFolder";
 
   public PropertyOutput(
       TransformMeta transformMeta,
@@ -53,7 +48,6 @@ public class PropertyOutput extends 
BaseTransform<PropertyOutputMeta, PropertyOu
 
   @Override
   public boolean processRow() throws HopException {
-
     Object[] r = getRow(); // this also waits for a previous transform to be 
finished.
 
     if (r == null) { // no more input to be expected...
@@ -63,70 +57,16 @@ public class PropertyOutput extends 
BaseTransform<PropertyOutputMeta, PropertyOu
 
     if (first) {
       first = false;
-      data.inputRowMeta = getInputRowMeta();
-      data.outputRowMeta = data.inputRowMeta.clone();
-      meta.getFields(data.outputRowMeta, getTransformName(), null, null, this, 
metadataProvider);
-
-      // Let's take the index of Key field ...
-      data.indexOfKeyField = 
data.inputRowMeta.indexOfValue(meta.getKeyField());
-      if (data.indexOfKeyField < 0) {
-        // The field is unreachable !
-        logError(
-            BaseMessages.getString(
-                PKG, CONST_PROPERTY_OUTPUT_LOG_ERROR_FINDING_FIELD, 
meta.getKeyField()));
-        throw new HopException(
-            BaseMessages.getString(
-                PKG, CONST_PROPERTY_OUTPUT_LOG_ERROR_FINDING_FIELD, 
meta.getKeyField()));
-      }
-
-      // Let's take the index of Key field ...
-      data.indexOfValueField = 
data.inputRowMeta.indexOfValue(meta.getValueField());
-      if (data.indexOfValueField < 0) {
-        // The field is unreachable !
-        logError(
-            BaseMessages.getString(
-                PKG, CONST_PROPERTY_OUTPUT_LOG_ERROR_FINDING_FIELD, 
meta.getValueField()));
-        throw new HopException(
-            BaseMessages.getString(
-                PKG, CONST_PROPERTY_OUTPUT_LOG_ERROR_FINDING_FIELD, 
meta.getValueField()));
-      }
-
-      if (meta.isFileNameInField()) {
-        String realFieldName = resolve(meta.getFileNameField());
-        if (Utils.isEmpty(realFieldName)) {
-          logError(BaseMessages.getString(PKG, 
"PropertyOutput.Log.FilenameInFieldEmpty"));
-          throw new HopException(
-              BaseMessages.getString(PKG, 
"PropertyOutput.Log.FilenameInFieldEmpty"));
-        }
-        data.indexOfFieldfilename = 
data.inputRowMeta.indexOfValue(realFieldName);
-        if (data.indexOfFieldfilename < 0) {
-          // The field is unreachable !
-          logError(
-              BaseMessages.getString(
-                  PKG, CONST_PROPERTY_OUTPUT_LOG_ERROR_FINDING_FIELD, 
meta.getValueField()));
-          throw new HopException(
-              BaseMessages.getString(
-                  PKG, CONST_PROPERTY_OUTPUT_LOG_ERROR_FINDING_FIELD, 
meta.getValueField()));
-        }
-      } else {
-        // Let's check for filename...
-        data.filename = buildFilename();
-        // Check if filename is empty..
-        if (Utils.isEmpty(data.filename)) {
-          logError(BaseMessages.getString(PKG, 
"PropertyOutput.Log.FilenameEmpty"));
-          throw new HopException(BaseMessages.getString(PKG, 
"PropertyOutput.Log.FilenameEmpty"));
-        }
-        openNewFile();
-      }
-    } // end first
+      processRowFirstCall();
+    }
 
     // Get value field
-    String propkey = data.inputRowMeta.getString(r, data.indexOfKeyField);
-    String propvalue = data.inputRowMeta.getString(r, data.indexOfValueField);
+    String propKey = data.inputRowMeta.getString(r, data.indexOfKeyField);
+    String propValue = data.inputRowMeta.getString(r, data.indexOfValueField);
 
     try {
       if (meta.isFileNameInField()) {
-        data.filename = data.inputRowMeta.getString(r, 
data.indexOfFieldfilename);
+        data.filename = data.inputRowMeta.getString(r, 
data.indexOfFieldFileName);
         if (Utils.isEmpty(data.filename)) {
           throw new HopException(
               BaseMessages.getString(PKG, 
"PropertyOutputMeta.Log.FileNameEmty"));
@@ -139,28 +79,20 @@ public class PropertyOutput extends 
BaseTransform<PropertyOutputMeta, PropertyOu
         }
       }
 
-      if (!data.KeySet.contains(propkey)) {
+      if (!data.keySet.contains(propKey)) {
         if (isDetailed()) {
-          logDetailed(BaseMessages.getString(PKG, "PropertyOutput.Log.Key", 
propkey));
-          logDetailed(BaseMessages.getString(PKG, "PropertyOutput.Log.Value", 
propvalue));
+          logDetailed(BaseMessages.getString(PKG, "PropertyOutput.Log.Key", 
propKey));
+          logDetailed(BaseMessages.getString(PKG, "PropertyOutput.Log.Value", 
propValue));
         }
         // Update property
-        data.pro.setProperty(propkey, propvalue);
+        data.properties.setProperty(propKey, propValue);
         putRow(data.outputRowMeta, r); // in case we want it to go further...
         incrementLinesOutput();
 
-        if (checkFeedback(getLinesRead()) && isBasic()) {
-          logBasic("linenr " + getLinesRead());
-        }
-        data.KeySet.add(propkey);
+        data.keySet.add(propKey);
       }
     } catch (HopTransformException e) {
-      boolean sendToErrorRow = false;
-      String errorMessage = null;
-      if (getTransformMeta().isDoingErrorHandling()) {
-        sendToErrorRow = true;
-        errorMessage = e.toString();
-      } else {
+      if (!getTransformMeta().isDoingErrorHandling()) {
         logError(
             BaseMessages.getString(PKG, 
"PropertyOutputMeta.Log.ErrorInTransform")
                 + e.getMessage());
@@ -169,25 +101,79 @@ public class PropertyOutput extends 
BaseTransform<PropertyOutputMeta, PropertyOu
         setOutputDone(); // signal end to receiver(s)
         return false;
       }
-      if (sendToErrorRow) {
-        putError(data.outputRowMeta, r, 1L, errorMessage, null, 
"PROPSOUTPUTO001");
-      }
+      putError(data.outputRowMeta, r, 1L, e.toString(), null, 
"PROPSOUTPUTO001");
     }
     return true;
   }
 
+  private void processRowFirstCall() throws HopException {
+    data.inputRowMeta = getInputRowMeta();
+    data.outputRowMeta = data.inputRowMeta.clone();
+    meta.getFields(data.outputRowMeta, getTransformName(), null, null, this, 
metadataProvider);
+
+    // Let's take the index of Key field ...
+    data.indexOfKeyField = data.inputRowMeta.indexOfValue(meta.getKeyField());
+    if (data.indexOfKeyField < 0) {
+      // The field is unreachable !
+      logError(
+          BaseMessages.getString(PKG, "PropertyOutput.Log.ErrorFindingField", 
meta.getKeyField()));
+      throw new HopException(
+          BaseMessages.getString(PKG, "PropertyOutput.Log.ErrorFindingField", 
meta.getKeyField()));
+    }
+
+    // Let's take the index of Key field ...
+    data.indexOfValueField = 
data.inputRowMeta.indexOfValue(meta.getValueField());
+    if (data.indexOfValueField < 0) {
+      // The field is unreachable !
+      logError(
+          BaseMessages.getString(
+              PKG, "PropertyOutput.Log.ErrorFindingField", 
meta.getValueField()));
+      throw new HopException(
+          BaseMessages.getString(
+              PKG, "PropertyOutput.Log.ErrorFindingField", 
meta.getValueField()));
+    }
+
+    if (meta.isFileNameInField()) {
+      String realFieldName = resolve(meta.getFileNameField());
+      if (Utils.isEmpty(realFieldName)) {
+        logError(BaseMessages.getString(PKG, 
"PropertyOutput.Log.FilenameInFieldEmpty"));
+        throw new HopException(
+            BaseMessages.getString(PKG, 
"PropertyOutput.Log.FilenameInFieldEmpty"));
+      }
+      data.indexOfFieldFileName = 
data.inputRowMeta.indexOfValue(realFieldName);
+      if (data.indexOfFieldFileName < 0) {
+        // The field is unreachable !
+        logError(
+            BaseMessages.getString(
+                PKG, "PropertyOutput.Log.ErrorFindingField", 
meta.getValueField()));
+        throw new HopException(
+            BaseMessages.getString(
+                PKG, "PropertyOutput.Log.ErrorFindingField", 
meta.getValueField()));
+      }
+    } else {
+      // Let's check for filename...
+      data.filename = buildFilename();
+      // Check if filename is empty..
+      if (Utils.isEmpty(data.filename)) {
+        logError(BaseMessages.getString(PKG, 
"PropertyOutput.Log.FilenameEmpty"));
+        throw new HopException(BaseMessages.getString(PKG, 
"PropertyOutput.Log.FilenameEmpty"));
+      }
+      openNewFile();
+    }
+  }
+
   public boolean checkSameFile() {
     return data.previousFileName.equals(data.filename);
   }
 
   private void openNewFile() throws HopException {
     try (FileObject newFile = HopVfs.getFileObject(data.filename, variables)) {
-      data.pro = new Properties();
-      data.KeySet.clear();
+      data.properties = new Properties();
+      data.keySet.clear();
 
       data.file = newFile;
-      if (meta.isAppend() && data.file.exists()) {
-        data.pro.load(HopVfs.getInputStream(data.file));
+      if (meta.getFileDetails().isAppending() && data.file.exists()) {
+        data.properties.load(HopVfs.getInputStream(data.file));
       }
       // Create parent folder if needed...
       createParentFolder();
@@ -199,66 +185,54 @@ public class PropertyOutput extends 
BaseTransform<PropertyOutputMeta, PropertyOu
   }
 
   private void createParentFolder() throws HopException {
-    if (meta.isCreateParentFolder()) {
-      FileObject parentfolder = null;
-      try {
-        // Do we need to create parent folder ?
-
-        // Check for parent folder
-        // Get parent folder
-        parentfolder = data.file.getParent();
-        if (!parentfolder.exists()) {
-          if (isDetailed()) {
-            logDetailed(
-                BaseMessages.getString(
-                    PKG,
-                    "PropertyOutput.Log.ParentFolderExists",
-                    parentfolder.getName().toString()));
-          }
-          parentfolder.createFolder();
-          if (isDetailed()) {
-            logDetailed(
-                BaseMessages.getString(
-                    PKG,
-                    CONST_PROPERTY_OUTPUT_LOG_CAN_NOT_CREATE_PARENT_FOLDER,
-                    parentfolder.getName().toString()));
-          }
+    if (!meta.getFileDetails().isCreateParentFolder()) {
+      return;
+    }
+    try (FileObject parentFolder = data.file.getParent()) {
+      // Do we need to create parent folder ?
+      // Check for parent folder
+      // Get parent folder
+      //
+      if (!parentFolder.exists()) {
+        if (isDetailed()) {
+          logDetailed(
+              BaseMessages.getString(
+                  PKG, "PropertyOutput.Log.ParentFolderExists", 
parentFolder.getName().toString()));
         }
-      } catch (Exception e) {
-        logError(
-            BaseMessages.getString(
-                PKG,
-                CONST_PROPERTY_OUTPUT_LOG_CAN_NOT_CREATE_PARENT_FOLDER,
-                parentfolder.getName().toString()));
-        throw new HopException(
-            BaseMessages.getString(
-                PKG,
-                CONST_PROPERTY_OUTPUT_LOG_CAN_NOT_CREATE_PARENT_FOLDER,
-                parentfolder.getName().toString()));
-      } finally {
-        if (parentfolder != null) {
-          try {
-            parentfolder.close();
-          } catch (Exception ex) {
-            /* Ignore */
-          }
+        parentFolder.createFolder();
+        if (isDetailed()) {
+          logDetailed(
+              BaseMessages.getString(
+                  PKG,
+                  "PropertyOutput.Log.ParentFolderCreated",
+                  parentFolder.getName().toString()));
         }
       }
+    } catch (Exception e) {
+      logError(
+          BaseMessages.getString(
+              PKG,
+              "PropertyOutput.Log.ParentFolderCouldNotBeCreated",
+              data.file.getName().toString()));
+      throw new HopException(
+          BaseMessages.getString(
+              PKG,
+              "PropertyOutput.Log.ParentFolderCouldNotBeCreated",
+              data.file.getName().toString()));
     }
   }
 
-  private boolean closeFile() {
+  private void closeFile() {
     if (data.file == null) {
-      return true;
+      return;
     }
-    boolean retval = false;
     try (OutputStream raw = HopVfs.getOutputStream(data.file, false);
         CountingOutputStream propsFile = new CountingOutputStream(raw)) {
-      data.pro.store(propsFile, resolve(meta.getComment()));
+      data.properties.store(propsFile, resolve(meta.getComment()));
 
       dataVolumeOut = (dataVolumeOut != null ? dataVolumeOut : 0L) + 
propsFile.getCount();
 
-      if (meta.isAddToResult()) {
+      if (meta.getFileDetails().isAddToResult()) {
         // Add this to the result file names...
         ResultFile resultFile =
             new ResultFile(
@@ -269,8 +243,7 @@ public class PropertyOutput extends 
BaseTransform<PropertyOutputMeta, PropertyOu
         resultFile.setComment(BaseMessages.getString(PKG, 
"PropertyOutput.Log.FileAddedResult"));
         addResultFile(resultFile);
       }
-      data.KeySet.clear();
-      retval = true;
+      data.keySet.clear();
     } catch (Exception e) {
       logError("Exception trying to close file [" + data.file.getName() + "]! 
:" + e.toString());
       setErrors(1);
@@ -284,31 +257,19 @@ public class PropertyOutput extends 
BaseTransform<PropertyOutputMeta, PropertyOu
           logDetailed("Exception trying to close file [" + data.file.getName() 
+ "]! :", e);
         }
       }
-      if (data.pro != null) {
-        data.pro = null;
+      if (data.properties != null) {
+        data.properties = null;
       }
     }
-    return retval;
   }
 
   public String buildFilename() {
     return meta.buildFilename(this, getCopy());
   }
 
-  @Override
-  public boolean init() {
-
-    if (super.init()) {
-      return true;
-    }
-    return false;
-  }
-
   @Override
   public void dispose() {
-
     closeFile();
-
     setOutputDone();
     super.dispose();
   }
diff --git 
a/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputData.java
 
b/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputData.java
index e67a1dc9d7..42b8d99dd0 100644
--- 
a/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputData.java
+++ 
b/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputData.java
@@ -21,6 +21,7 @@ import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.HashSet;
 import java.util.Properties;
+import java.util.Set;
 import org.apache.commons.vfs2.FileObject;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.pipeline.transform.BaseTransformData;
@@ -37,12 +38,12 @@ public class PropertyOutputData extends BaseTransformData 
implements ITransformD
   public int indexOfKeyField;
   public int indexOfValueField;
 
-  public int indexOfFieldfilename;
-  public HashSet<String> KeySet;
+  public int indexOfFieldFileName;
+  public Set<String> keySet;
   public FileObject file;
   public String filename;
 
-  public Properties pro;
+  public Properties properties;
 
   public String previousFileName;
 
@@ -54,10 +55,10 @@ public class PropertyOutputData extends BaseTransformData 
implements ITransformD
     indexOfKeyField = -1;
     indexOfValueField = -1;
 
-    indexOfFieldfilename = -1;
+    indexOfFieldFileName = -1;
     file = null;
     previousFileName = "";
-    KeySet = new HashSet<>();
+    keySet = new HashSet<>();
     filename = null;
   }
 }
diff --git 
a/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputDialog.java
 
b/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputDialog.java
index 68ceaafde5..1908e0e3b5 100644
--- 
a/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputDialog.java
+++ 
b/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputDialog.java
@@ -17,6 +17,7 @@
 
 package org.apache.hop.pipeline.transforms.propertyoutput;
 
+import org.apache.hop.core.Const;
 import org.apache.hop.core.Props;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.row.IRowMeta;
@@ -280,6 +281,7 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
     fdbFilename.right = new FormAttachment(100, 0);
     fdbFilename.top = new FormAttachment(wFields, 0);
     wbFilename.setLayoutData(fdbFilename);
+    wbFilename.addListener(SWT.Selection, e -> browseForFiles());
 
     wFilename = new TextVar(variables, wFileName, SWT.SINGLE | SWT.LEFT | 
SWT.BORDER);
     PropsUi.setLook(wFilename);
@@ -307,13 +309,7 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
     fdAppend.top = new FormAttachment(wlAppend, 0, SWT.CENTER);
     fdAppend.right = new FormAttachment(100, 0);
     wAppend.setLayoutData(fdAppend);
-    wAppend.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent arg0) {
-            input.setChanged();
-          }
-        });
+    wAppend.addListener(SWT.Selection, e -> input.setChanged());
 
     // Create Parent Folder
     Label wlCreateParentFolder = new Label(wFileName, SWT.RIGHT);
@@ -334,13 +330,7 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
     fdCreateParentFolder.top = new FormAttachment(wlCreateParentFolder, 0, 
SWT.CENTER);
     fdCreateParentFolder.right = new FormAttachment(100, 0);
     wCreateParentFolder.setLayoutData(fdCreateParentFolder);
-    wCreateParentFolder.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            input.setChanged();
-          }
-        });
+    wCreateParentFolder.addListener(SWT.Selection, e -> input.setChanged());
 
     // FileNameInField line
     Label wlFileNameInField = new Label(wFileName, SWT.RIGHT);
@@ -361,13 +351,11 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
     fdFileNameInField.top = new FormAttachment(wlFileNameInField, 0, 
SWT.CENTER);
     fdFileNameInField.right = new FormAttachment(100, 0);
     wFileNameInField.setLayoutData(fdFileNameInField);
-    wFileNameInField.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            input.setChanged();
-            activateFilenameInField();
-          }
+    wFileNameInField.addListener(
+        SWT.Selection,
+        e -> {
+          input.setChanged();
+          activateFilenameInField();
         });
 
     // FileNameField Line
@@ -411,7 +399,7 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
     fdExtension.right = new FormAttachment(100, -margin);
     wExtension.setLayoutData(fdExtension);
 
-    // Create multi-part file?
+    // Create multipart file?
     wlAddTransformNr = new Label(wFileName, SWT.RIGHT);
     wlAddTransformNr.setText(
         BaseMessages.getString(PKG, 
"PropertyOutputDialog.AddTransformnr.Label"));
@@ -428,15 +416,9 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
     fdAddTransformNr.top = new FormAttachment(wlAddTransformNr, 0, SWT.CENTER);
     fdAddTransformNr.right = new FormAttachment(100, 0);
     wAddTransformNr.setLayoutData(fdAddTransformNr);
-    wAddTransformNr.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            input.setChanged();
-          }
-        });
+    wAddTransformNr.addListener(SWT.Selection, e -> input.setChanged());
 
-    // Create multi-part file?
+    // Create multipart file?
     wlAddDate = new Label(wFileName, SWT.RIGHT);
     wlAddDate.setText(BaseMessages.getString(PKG, 
"PropertyOutputDialog.AddDate.Label"));
     PropsUi.setLook(wlAddDate);
@@ -459,7 +441,7 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
             input.setChanged();
           }
         });
-    // Create multi-part file?
+    // Create multipart file?
     wlAddTime = new Label(wFileName, SWT.RIGHT);
     wlAddTime.setText(BaseMessages.getString(PKG, 
"PropertyOutputDialog.AddTime.Label"));
     PropsUi.setLook(wlAddTime);
@@ -475,13 +457,7 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
     fdAddTime.top = new FormAttachment(wlAddTime, 0, SWT.CENTER);
     fdAddTime.right = new FormAttachment(100, 0);
     wAddTime.setLayoutData(fdAddTime);
-    wAddTime.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            input.setChanged();
-          }
-        });
+    wAddTime.addListener(SWT.Selection, e -> input.setChanged());
 
     wbShowFiles = new Button(wFileName, SWT.PUSH | SWT.CENTER);
     PropsUi.setLook(wbShowFiles);
@@ -490,33 +466,7 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
     fdbShowFiles.left = new FormAttachment(middle, 0);
     fdbShowFiles.top = new FormAttachment(wAddTime, margin);
     wbShowFiles.setLayoutData(fdbShowFiles);
-    wbShowFiles.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            PropertyOutputMeta tfoi = new PropertyOutputMeta();
-            getInfo(tfoi);
-            String[] files = tfoi.getFiles(variables);
-            if (files != null && files.length > 0) {
-              EnterSelectionDialog esd =
-                  new EnterSelectionDialog(
-                      shell,
-                      files,
-                      BaseMessages.getString(
-                          PKG, 
"PropertyOutputDialog.SelectOutputFiles.DialogTitle"),
-                      BaseMessages.getString(
-                          PKG, 
"PropertyOutputDialog.SelectOutputFiles.DialogMessage"));
-              esd.setViewOnly();
-              esd.open();
-            } else {
-              MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR);
-              mb.setMessage(
-                  BaseMessages.getString(PKG, 
"PropertyOutputDialog.NoFilesFound.DialogMessage"));
-              mb.setText(BaseMessages.getString(PKG, 
"System.DialogTitle.Error"));
-              mb.open();
-            }
-          }
-        });
+    wbShowFiles.addListener(SWT.Selection, e -> showFiles());
 
     FormData fdFileName = new FormData();
     fdFileName.left = new FormAttachment(0, margin);
@@ -561,14 +511,7 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
     fdAddToResult.top = new FormAttachment(wlAddToResult, 0, SWT.CENTER);
     fdAddToResult.right = new FormAttachment(100, 0);
     wAddToResult.setLayoutData(fdAddToResult);
-    SelectionAdapter lsSelAR =
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent arg0) {
-            input.setChanged();
-          }
-        };
-    wAddToResult.addSelectionListener(lsSelAR);
+    wAddToResult.addListener(SWT.Selection, e -> input.setChanged());
 
     FormData fdResultFile = new FormData();
     fdResultFile.left = new FormAttachment(0, margin);
@@ -585,7 +528,7 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
     fdContentComp.top = new FormAttachment(0, 0);
     fdContentComp.right = new FormAttachment(100, 0);
     fdContentComp.bottom = new FormAttachment(100, 0);
-    wContentComp.setLayoutData(wContentComp);
+    wContentComp.setLayoutData(fdContentComp);
 
     wContentComp.layout();
     wContentTab.setControl(wContentComp);
@@ -601,23 +544,6 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
     fdTabFolder.bottom = new FormAttachment(wOk, -margin);
     wTabFolder.setLayoutData(fdTabFolder);
 
-    // Add listeners
-    wbFilename.addListener(
-        SWT.Selection,
-        e ->
-            BaseDialog.presentFileDialog(
-                true,
-                shell,
-                wFilename,
-                variables,
-                new String[] {"*.txt", "*.csv", "*"},
-                new String[] {
-                  BaseMessages.getString(PKG, "System.FileType.TextFiles"),
-                  BaseMessages.getString(PKG, "System.FileType.CSVFiles"),
-                  BaseMessages.getString(PKG, "System.FileType.AllFiles")
-                },
-                true));
-
     wTabFolder.setSelection(0);
 
     getData();
@@ -630,6 +556,42 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
     return transformName;
   }
 
+  private void showFiles() {
+    PropertyOutputMeta propertyOutputMeta = new PropertyOutputMeta();
+    getInfo(propertyOutputMeta);
+    String[] files = propertyOutputMeta.getFiles(variables);
+    if (files != null && files.length > 0) {
+      EnterSelectionDialog esd =
+          new EnterSelectionDialog(
+              shell,
+              files,
+              BaseMessages.getString(PKG, 
"PropertyOutputDialog.SelectOutputFiles.DialogTitle"),
+              BaseMessages.getString(PKG, 
"PropertyOutputDialog.SelectOutputFiles.DialogMessage"));
+      esd.setViewOnly();
+      esd.open();
+    } else {
+      MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR);
+      mb.setMessage(BaseMessages.getString(PKG, 
"PropertyOutputDialog.NoFilesFound.DialogMessage"));
+      mb.setText(BaseMessages.getString(PKG, "System.DialogTitle.Error"));
+      mb.open();
+    }
+  }
+
+  private void browseForFiles() {
+    BaseDialog.presentFileDialog(
+        true,
+        shell,
+        wFilename,
+        variables,
+        new String[] {"*.txt", "*.csv", "*"},
+        new String[] {
+          BaseMessages.getString(PKG, "System.FileType.TextFiles"),
+          BaseMessages.getString(PKG, "System.FileType.CSVFiles"),
+          BaseMessages.getString(PKG, "System.FileType.AllFiles")
+        },
+        true);
+  }
+
   private void activateFilenameInField() {
     wlFileNameField.setEnabled(wFileNameInField.getSelection());
     wFileNameField.setEnabled(wFileNameInField.getSelection());
@@ -667,35 +629,21 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
 
   /** Copy information from the meta-data input to the dialog fields. */
   public void getData() {
-    if (input.getKeyField() != null) {
-      wKeyField.setText(input.getKeyField());
-    }
-    if (input.getValueField() != null) {
-      wValueField.setText(input.getValueField());
-    }
-
-    if (input.getFileName() != null) {
-      wFilename.setText(input.getFileName());
-    }
+    wKeyField.setText(Const.NVL(input.getKeyField(), ""));
+    wValueField.setText(Const.NVL(input.getValueField(), ""));
+    wFilename.setText(Const.NVL(input.getFileDetails().getFileName(), ""));
     wFileNameInField.setSelection(input.isFileNameInField());
-    if (input.getFileNameField() != null) {
-      wFileNameField.setText(input.getFileNameField());
-    }
-    wCreateParentFolder.setSelection(input.isCreateParentFolder());
-    if (input.getExtension() != null) {
-      wExtension.setText(input.getExtension());
-    }
-
-    wAddDate.setSelection(input.isDateInFilename());
-    wAddTime.setSelection(input.isTimeInFilename());
-    wAddTransformNr.setSelection(input.isTransformNrInFilename());
+    wFileNameField.setText(Const.NVL(input.getFileNameField(), ""));
+    
wCreateParentFolder.setSelection(input.getFileDetails().isCreateParentFolder());
+    wExtension.setText(Const.NVL(input.getFileDetails().getExtension(), ""));
 
-    wAddToResult.setSelection(input.isAddToResult());
-    wAppend.setSelection(input.isAppend());
+    wAddDate.setSelection(input.getFileDetails().isDateInFilename());
+    wAddTime.setSelection(input.getFileDetails().isTimeInFilename());
+    
wAddTransformNr.setSelection(input.getFileDetails().isTransformNrInFilename());
 
-    if (input.getComment() != null) {
-      wComment.setText(input.getComment());
-    }
+    wAddToResult.setSelection(input.getFileDetails().isAddToResult());
+    wAppend.setSelection(input.getFileDetails().isAppending());
+    wComment.setText(Const.NVL(input.getComment(), ""));
   }
 
   private void cancel() {
@@ -707,16 +655,16 @@ public class PropertyOutputDialog extends 
BaseTransformDialog {
   private void getInfo(PropertyOutputMeta info) {
     info.setKeyField(wKeyField.getText());
     info.setValueField(wValueField.getText());
-    info.setCreateParentFolder(wCreateParentFolder.getSelection());
-    info.setAppend(wAppend.getSelection());
-    info.setFileName(wFilename.getText());
-    info.setExtension(wExtension.getText());
-    info.setTransformNrInFilename(wAddTransformNr.getSelection());
-    info.setDateInFilename(wAddDate.getSelection());
-    info.setTimeInFilename(wAddTime.getSelection());
+    
info.getFileDetails().setCreateParentFolder(wCreateParentFolder.getSelection());
+    info.getFileDetails().setAppending(wAppend.getSelection());
+    info.getFileDetails().setFileName(wFilename.getText());
+    info.getFileDetails().setExtension(wExtension.getText());
+    
info.getFileDetails().setTransformNrInFilename(wAddTransformNr.getSelection());
+    info.getFileDetails().setDateInFilename(wAddDate.getSelection());
+    info.getFileDetails().setTimeInFilename(wAddTime.getSelection());
+    info.getFileDetails().setAddToResult(wAddToResult.getSelection());
     info.setFileNameField(wFileNameField.getText());
     info.setFileNameInField(wFileNameInField.getSelection());
-    info.setAddToResult(wAddToResult.getSelection());
 
     info.setComment(wComment.getText());
   }
diff --git 
a/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputMeta.java
 
b/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputMeta.java
index 53fb0dc414..befb8f0e0a 100644
--- 
a/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputMeta.java
+++ 
b/plugins/transforms/propertyoutput/src/main/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputMeta.java
@@ -21,13 +21,14 @@ import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.commons.vfs2.FileObject;
 import org.apache.hop.core.CheckResult;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.ICheckResult;
 import org.apache.hop.core.annotations.Transform;
 import org.apache.hop.core.exception.HopException;
-import org.apache.hop.core.exception.HopXmlException;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.util.Utils;
@@ -35,6 +36,7 @@ import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.core.vfs.HopVfs;
 import org.apache.hop.core.xml.XmlHandler;
 import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.transform.BaseTransformMeta;
@@ -51,216 +53,114 @@ import org.w3c.dom.Node;
     categoryDescription = 
"i18n:org.apache.hop.pipeline.transform:BaseTransform.Category.Output",
     keywords = "i18n::PropertyOutputMeta.keyword",
     documentationUrl = "/pipeline/transforms/propertyoutput.html")
+@Getter
+@Setter
 public class PropertyOutputMeta extends BaseTransformMeta<PropertyOutput, 
PropertyOutputData> {
   private static final Class<?> PKG = PropertyOutputMeta.class;
   public static final String CONST_SPACES_LONG = "      ";
   public static final String CONST_SPACES = "    ";
 
-  private String keyfield;
-  private String valuefield;
+  @Getter
+  @Setter
+  public static class FileDetails {
+    @HopMetadataProperty(key = "name")
+    private String fileName;
+
+    /** The file extension in case of a generated filename */
+    @HopMetadataProperty(key = "extension")
+    private String extension;
+
+    /** Flag: add the transform copy number in the filename */
+    @HopMetadataProperty(key = "split")
+    private boolean transformNrInFilename;
+
+    /** Flag: add the partition ID in the filename */
+    @HopMetadataProperty(key = "haspartno")
+    private boolean partitionIdInFilename;
+
+    /** Flag: add the date in the filename */
+    @HopMetadataProperty(key = "add_date")
+    private boolean dateInFilename;
+
+    /** Flag: add the time in the filename */
+    @HopMetadataProperty(key = "add_time")
+    private boolean timeInFilename;
+
+    /** Flag: create parent folder if needed */
+    @HopMetadataProperty(key = "create_parent_folder")
+    private boolean createParentFolder;
+
+    /** Flag append in file */
+    @HopMetadataProperty(key = "append")
+    private boolean appending;
+
+    @HopMetadataProperty(key = "addtoresult")
+    private boolean addToResult;
+
+    public FileDetails() {}
+
+    public FileDetails(FileDetails f) {
+      this();
+      this.appending = f.appending;
+      this.createParentFolder = f.createParentFolder;
+      this.dateInFilename = f.dateInFilename;
+      this.extension = f.extension;
+      this.fileName = f.fileName;
+      this.partitionIdInFilename = f.partitionIdInFilename;
+      this.timeInFilename = f.timeInFilename;
+      this.transformNrInFilename = f.transformNrInFilename;
+      this.addToResult = f.addToResult;
+    }
+  }
 
-  private boolean addToResult;
+  @HopMetadataProperty(key = "keyfield")
+  private String keyField;
 
-  /** The base name of the output file */
-  private String fileName;
+  @HopMetadataProperty(key = "valuefield")
+  private String valueField;
 
-  /* Specification if file name is in field */
+  /** Comment to add in file */
+  @HopMetadataProperty(key = "comment")
+  private String comment;
 
+  /** Specification if file name is in field */
+  @HopMetadataProperty(key = "fileNameInField")
   private boolean fileNameInField;
 
+  @HopMetadataProperty(key = "fileNameField")
   private String fileNameField;
 
-  /** The file extention in case of a generated filename */
-  private String extension;
-
-  /** Flag: add the transformnr in the filename */
-  private boolean transformNrInFilename;
-
-  /** Flag: add the partition number in the filename */
-  private boolean partNrInFilename;
-
-  /** Flag: add the date in the filename */
-  private boolean dateInFilename;
-
-  /** Flag: add the time in the filename */
-  private boolean timeInFilename;
-
-  /** Flag: create parent folder if needed */
-  private boolean createparentfolder;
+  @HopMetadataProperty(key = "file")
+  private FileDetails fileDetails;
 
-  /** Comment to add in file */
-  private String comment;
-
-  /** Flag append in file */
-  private boolean append;
+  public PropertyOutputMeta() {
+    this.fileDetails = new FileDetails();
+  }
 
-  @Override
-  public void loadXml(Node transformNode, IHopMetadataProvider 
metadataProvider)
-      throws HopXmlException {
-    readData(transformNode);
+  public PropertyOutputMeta(PropertyOutputMeta m) {
+    this();
+    this.comment = m.comment;
+    this.keyField = m.keyField;
+    this.valueField = m.valueField;
+    this.fileNameInField = m.fileNameInField;
+    this.fileNameField = m.fileNameField;
+    this.fileDetails = new FileDetails(m.fileDetails);
   }
 
   @Override
   public Object clone() {
-
-    PropertyOutputMeta retval = (PropertyOutputMeta) super.clone();
-    return retval;
-  }
-
-  /**
-   * @return Returns the extension.
-   */
-  public String getExtension() {
-    return extension;
-  }
-
-  /**
-   * @param extension The extension to set.
-   */
-  public void setExtension(String extension) {
-    this.extension = extension;
-  }
-
-  /**
-   * @return Returns the fileName.
-   */
-  public String getFileName() {
-    return fileName;
-  }
-
-  /**
-   * @return Is the file name coded in a field?
-   */
-  public boolean isFileNameInField() {
-    return fileNameInField;
-  }
-
-  /**
-   * @param fileNameInField Is the file name coded in a field?
-   */
-  public void setFileNameInField(boolean fileNameInField) {
-    this.fileNameInField = fileNameInField;
-  }
-
-  /**
-   * @return The field name that contains the output file name.
-   */
-  public String getFileNameField() {
-    return fileNameField;
-  }
-
-  /**
-   * @param fileNameField Name of the field that contains the file name
-   */
-  public void setFileNameField(String fileNameField) {
-    this.fileNameField = fileNameField;
-  }
-
-  /**
-   * @return Returns the transformNrInFilename.
-   */
-  public boolean isTransformNrInFilename() {
-    return transformNrInFilename;
-  }
-
-  /**
-   * @param transformNrInFilename The transformNrInFilename to set.
-   */
-  public void setTransformNrInFilename(boolean transformNrInFilename) {
-    this.transformNrInFilename = transformNrInFilename;
-  }
-
-  /**
-   * @return Returns the timeInFilename.
-   */
-  public boolean isTimeInFilename() {
-    return timeInFilename;
-  }
-
-  /**
-   * @return Returns the dateInFilename.
-   */
-  public boolean isDateInFilename() {
-    return dateInFilename;
-  }
-
-  /**
-   * @param dateInFilename The dateInFilename to set.
-   */
-  public void setDateInFilename(boolean dateInFilename) {
-    this.dateInFilename = dateInFilename;
-  }
-
-  /**
-   * @param timeInFilename The timeInFilename to set.
-   */
-  public void setTimeInFilename(boolean timeInFilename) {
-    this.timeInFilename = timeInFilename;
-  }
-
-  /**
-   * @param fileName The fileName to set.
-   */
-  public void setFileName(String fileName) {
-    this.fileName = fileName;
-  }
-
-  public boolean isAddToResult() {
-    return addToResult;
-  }
-
-  /**
-   * @param addToResult The Add file to result to set.
-   */
-  public void setAddToResult(boolean addToResult) {
-    this.addToResult = addToResult;
-  }
-
-  /**
-   * @return Returns the create parent folder flag.
-   */
-  public boolean isCreateParentFolder() {
-    return createparentfolder;
-  }
-
-  /**
-   * @param createparentfolder The create parent folder flag to set.
-   */
-  public void setCreateParentFolder(boolean createparentfolder) {
-    this.createparentfolder = createparentfolder;
-  }
-
-  /**
-   * @return Returns the append flag.
-   */
-  public boolean isAppend() {
-    return append;
-  }
-
-  /**
-   * @param append The append to set.
-   */
-  public void setAppend(boolean append) {
-    this.append = append;
-  }
-
-  public String getComment() {
-    return comment;
-  }
-
-  public void setComment(String commentin) {
-    this.comment = commentin;
+    return new PropertyOutputMeta(this);
   }
 
   public String[] getFiles(IVariables variables) {
     int copies = 1;
     int parts = 1;
 
-    if (transformNrInFilename) {
+    if (fileDetails.transformNrInFilename) {
       copies = 3;
     }
 
-    if (partNrInFilename) {
+    if (fileDetails.partitionIdInFilename) {
       parts = 3;
     }
 
@@ -269,131 +169,52 @@ public class PropertyOutputMeta extends 
BaseTransformMeta<PropertyOutput, Proper
       nr++;
     }
 
-    String[] retval = new String[nr];
+    String[] fileNames = new String[nr];
 
     int i = 0;
     for (int copy = 0; copy < copies; copy++) {
       for (int part = 0; part < parts; part++) {
 
-        retval[i] = buildFilename(variables, copy);
+        fileNames[i] = buildFilename(variables, copy);
         i++;
       }
     }
     if (i < nr) {
-      retval[i] = "...";
+      fileNames[i] = "...";
     }
 
-    return retval;
+    return fileNames;
   }
 
-  public String buildFilename(IVariables variables, int transformnr) {
-
+  public String buildFilename(IVariables variables, int copyNr) {
     SimpleDateFormat daf = new SimpleDateFormat();
 
     // Replace possible environment variables...
-    String retval = variables.resolve(fileName);
+    String retval = variables.resolve(fileDetails.fileName);
 
     Date now = new Date();
 
-    if (dateInFilename) {
+    if (fileDetails.dateInFilename) {
       daf.applyPattern("yyyMMdd");
       String d = daf.format(now);
       retval += "_" + d;
     }
-    if (timeInFilename) {
+    if (fileDetails.timeInFilename) {
       daf.applyPattern("HHmmss");
       String t = daf.format(now);
       retval += "_" + t;
     }
-    if (transformNrInFilename) {
-      retval += "_" + transformnr;
+    if (fileDetails.transformNrInFilename) {
+      retval += "_" + copyNr;
     }
 
-    if (!Utils.isEmpty(extension)) {
-      retval += "." + extension;
+    if (!Utils.isEmpty(fileDetails.extension)) {
+      retval += "." + fileDetails.extension;
     }
 
     return retval;
   }
 
-  private void readData(Node transformNode) throws HopXmlException {
-    try {
-
-      keyfield = XmlHandler.getTagValue(transformNode, "keyfield");
-      valuefield = XmlHandler.getTagValue(transformNode, "valuefield");
-      comment = XmlHandler.getTagValue(transformNode, "comment");
-
-      fileName = XmlHandler.getTagValue(transformNode, "file", "name");
-
-      createparentfolder =
-          "Y"
-              .equalsIgnoreCase(
-                  XmlHandler.getTagValue(transformNode, "file", 
"create_parent_folder"));
-      extension =
-          Const.NVL(
-              XmlHandler.getTagValue(transformNode, "file", "extention"),
-              XmlHandler.getTagValue(transformNode, "file", "extension"));
-      transformNrInFilename =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, "file", 
"split"));
-      partNrInFilename =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, "file", 
"haspartno"));
-      dateInFilename =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, "file", 
"add_date"));
-      timeInFilename =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, "file", 
"add_time"));
-      addToResult =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, "file", 
"AddToResult"));
-      append = "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, 
"file", "append"));
-      fileName = XmlHandler.getTagValue(transformNode, "file", "name");
-      fileNameInField =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, 
"fileNameInField"));
-      fileNameField = XmlHandler.getTagValue(transformNode, "fileNameField");
-
-    } catch (Exception e) {
-      throw new HopXmlException("Unable to load transform info from XML", e);
-    }
-  }
-
-  @Override
-  public void setDefault() {
-    append = false;
-    createparentfolder = false;
-    // Items ...
-    keyfield = null;
-    valuefield = null;
-    comment = null;
-  }
-
-  @Override
-  public String getXml() {
-    StringBuilder retval = new StringBuilder();
-
-    // Items ...
-
-    retval.append(CONST_SPACES + XmlHandler.addTagValue("keyfield", keyfield));
-    retval.append(CONST_SPACES + XmlHandler.addTagValue("valuefield", 
valuefield));
-    retval.append(CONST_SPACES + XmlHandler.addTagValue("comment", comment));
-
-    retval.append(CONST_SPACES + XmlHandler.addTagValue("fileNameInField", 
fileNameInField));
-    retval.append(CONST_SPACES + XmlHandler.addTagValue("fileNameField", 
fileNameField));
-    retval.append("    <file>" + Const.CR);
-
-    retval.append(CONST_SPACES_LONG + XmlHandler.addTagValue("name", 
fileName));
-    retval.append(CONST_SPACES_LONG + XmlHandler.addTagValue("extention", 
extension));
-    retval.append(CONST_SPACES_LONG + XmlHandler.addTagValue("split", 
transformNrInFilename));
-    retval.append(CONST_SPACES_LONG + XmlHandler.addTagValue("haspartno", 
partNrInFilename));
-    retval.append(CONST_SPACES_LONG + XmlHandler.addTagValue("add_date", 
dateInFilename));
-    retval.append(CONST_SPACES_LONG + XmlHandler.addTagValue("add_time", 
timeInFilename));
-
-    retval.append(
-        CONST_SPACES_LONG + XmlHandler.addTagValue("create_parent_folder", 
createparentfolder));
-    retval.append(CONST_SPACES + XmlHandler.addTagValue("addtoresult", 
addToResult));
-    retval.append(CONST_SPACES + XmlHandler.addTagValue("append", append));
-    retval.append("      </file>" + Const.CR);
-
-    return retval.toString();
-  }
-
   @Override
   public void check(
       List<ICheckResult> remarks,
@@ -443,7 +264,7 @@ public class PropertyOutputMeta extends 
BaseTransformMeta<PropertyOutput, Proper
     }
 
     // Check if filename is given
-    if (!Utils.isEmpty(fileName)) {
+    if (!Utils.isEmpty(fileDetails.fileName)) {
       cr =
           new CheckResult(
               ICheckResult.TYPE_RESULT_OK,
@@ -461,7 +282,7 @@ public class PropertyOutputMeta extends 
BaseTransformMeta<PropertyOutput, Proper
 
     // Check for Key field
 
-    IValueMeta v = prev.searchValueMeta(keyfield);
+    IValueMeta v = prev.searchValueMeta(keyField);
     if (v == null) {
       cr =
           new CheckResult(
@@ -480,7 +301,7 @@ public class PropertyOutputMeta extends 
BaseTransformMeta<PropertyOutput, Proper
 
     // Check for Value field
 
-    v = prev.searchValueMeta(valuefield);
+    v = prev.searchValueMeta(valueField);
     if (v == null) {
       cr =
           new CheckResult(
@@ -498,34 +319,6 @@ public class PropertyOutputMeta extends 
BaseTransformMeta<PropertyOutput, Proper
     }
   }
 
-  /**
-   * @return the keyfield
-   */
-  public String getKeyField() {
-    return keyfield;
-  }
-
-  /**
-   * @return the valuefield
-   */
-  public String getValueField() {
-    return valuefield;
-  }
-
-  /**
-   * @param keyField the keyfield to set
-   */
-  public void setKeyField(String keyField) {
-    this.keyfield = keyField;
-  }
-
-  /**
-   * @param valuefield the valuefield to set
-   */
-  public void setValueField(String valuefield) {
-    this.valuefield = valuefield;
-  }
-
   @Override
   public boolean supportsErrorHandling() {
     return true;
@@ -538,8 +331,8 @@ public class PropertyOutputMeta extends 
BaseTransformMeta<PropertyOutput, Proper
    * pray that the file is on a shared drive or something like that.
    *
    * @param variables the variable variables to use
-   * @param definitions
-   * @param iResourceNaming
+   * @param definitions The definitions to use.
+   * @param iResourceNaming The way to rename resources.
    * @param metadataProvider the metadataProvider in which non-hop metadata 
could reside.
    * @return the filename of the exported resource
    */
@@ -559,15 +352,15 @@ public class PropertyOutputMeta extends 
BaseTransformMeta<PropertyOutput, Proper
       //
       // In case the name of the file comes from previous transforms, forget 
about this!
       if (!fileNameInField) {
-        FileObject fileObject = 
HopVfs.getFileObject(variables.resolve(fileName));
+        FileObject fileObject = 
HopVfs.getFileObject(variables.resolve(fileDetails.fileName));
 
         // If the file doesn't exist, forget about this effort too!
         //
         if (fileObject.exists()) {
           // Convert to an absolute path...
           //
-          fileName = iResourceNaming.nameResource(fileObject, variables, true);
-          return fileName;
+          fileDetails.fileName = iResourceNaming.nameResource(fileObject, 
variables, true);
+          return fileDetails.fileName;
         }
       }
       return null;
@@ -575,4 +368,17 @@ public class PropertyOutputMeta extends 
BaseTransformMeta<PropertyOutput, Proper
       throw new HopException(e);
     }
   }
+
+  /**
+   * This is used to provide backward compatibility with older XML files.
+   *
+   * @param node The node containing the transform properties
+   */
+  @Override
+  public void convertLegacyXml(Node node) {
+    fileDetails.extension =
+        Const.NVL(
+            XmlHandler.getTagValue(node, "file", "extention"),
+            XmlHandler.getTagValue(node, "file", "extension"));
+  }
 }
diff --git 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_en_US.properties
 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_en_US.properties
index 8088dc2d1d..14e7b85cba 100644
--- 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_en_US.properties
+++ 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_en_US.properties
@@ -16,7 +16,8 @@
 #
 
 PropertyOutput.Description=Write data to properties file
-PropertyOutput.Log.CanNotCreateParentFolder=Parent folder [{0}] was created.
+PropertyOutput.Log.ParentFolderCreated=Parent folder [{0}] was created.
+PropertyOutput.Log.ParentFolderCouldNotBeCreated=There was an error creating 
parent folder for file [{0}]\:
 PropertyOutput.Log.ErrorFindingField=The field {0} can not be found in the 
input stream\!
 PropertyOutput.Log.FileAddedResult=File [{0}] was added to result filenames
 PropertyOutput.Log.FilenameEmpty=Please specify filename \!
diff --git 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_es_AR.properties
 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_es_AR.properties
index 7f25e5399b..f26f6b130c 100644
--- 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_es_AR.properties
+++ 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_es_AR.properties
@@ -18,7 +18,7 @@
 #
 #
 
-PropertyOutput.Log.CanNotCreateParentFolder=El directorio padre [{0}] ha sido 
creado.
+PropertyOutput.Log.ParentFolderCreated=El directorio padre [{0}] ha sido 
creado.
 PropertyOutput.Log.ErrorFindingField=El campo {0} no puede ser encontrada en 
el flujo de entrada.
 PropertyOutput.Log.FileAddedResult=El archivo [{0}] fue a\u00F1adido a los 
archivos resultado.
 PropertyOutput.Log.FilenameEmpty=Por favor seleccione un archivo.
diff --git 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_fr_FR.properties
 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_fr_FR.properties
index 30cb43170a..bf244e9c6d 100644
--- 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_fr_FR.properties
+++ 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_fr_FR.properties
@@ -19,7 +19,7 @@
 #
 
 PropertyOutput.Description=Ecriture de valeurs (Cl\u00E9/Valeur) dans un 
fichier de propri\u00E9t\u00E9s (.properties)
-PropertyOutput.Log.CanNotCreateParentFolder=Le r\u00E9pertoire parent [{0}] a 
\u00E9t\u00E9 cr\u00E9e
+PropertyOutput.Log.ParentFolderCreated=Le r\u00E9pertoire parent [{0}] a 
\u00E9t\u00E9 cr\u00E9e
 PropertyOutput.Log.ErrorFindingField=Le champ {0} est introuvable dans le flux 
d''entr\u00E9e\!
 PropertyOutput.Log.FileAddedResult=Le nom de fichier [{0}] a \u00E9t\u00E9 
ajout\u00E9 \u00E0 la liste des fichiers li\u00E9e au r\u00E9sultat
 PropertyOutput.Log.FilenameEmpty=Veuillez svp sp\u00E9cifier le nom de fichier 
\!
diff --git 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_it_IT.properties
 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_it_IT.properties
index 61c8881bc2..d1892569e0 100644
--- 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_it_IT.properties
+++ 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_it_IT.properties
@@ -18,7 +18,7 @@
 #
 #
 
-PropertyOutput.Log.CanNotCreateParentFolder=La cartella padre [{0}] \u00E8 
stata creata.
+PropertyOutput.Log.ParentFolderCreated=La cartella padre [{0}] \u00E8 stata 
creata.
 PropertyOutput.Log.ErrorFindingField=Il campo {0} non \u00E8 stato trovato 
nell''input stream\!
 PropertyOutput.Log.FileAddedResult=Il file [{0}] \u00E8 stato aggiunto al file 
del risultato
 PropertyOutput.Log.FilenameEmpty=Prego specificare il nomefile\!
diff --git 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_ja_JP.properties
 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_ja_JP.properties
index d78aa2c627..6ddbc573fe 100644
--- 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_ja_JP.properties
+++ 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_ja_JP.properties
@@ -19,7 +19,7 @@
 #
 
 PropertyOutput.Description=Properties 
output\n\u30D7\u30ED\u30D1\u30C6\u30A3\u30D5\u30A1\u30A4\u30EB\u306B\u30C7\u30FC\u30BF\u3092\u66F8\u304D\u8FBC\u307F\u307E\u3059\u3002
-PropertyOutput.Log.CanNotCreateParentFolder=Parent folder [{0}] was created.
+PropertyOutput.Log.ParentFolderCreated=Parent folder [{0}] was created.
 PropertyOutput.Log.ErrorFindingField=The field {0} can not be found in the 
input stream\!
 PropertyOutput.Log.FileAddedResult=File [{0}] was added to result filenames
 PropertyOutput.Log.FilenameEmpty=Please specify filename \!
diff --git 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_ko_KR.properties
 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_ko_KR.properties
index 14985e5ef2..7da35f9adf 100644
--- 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_ko_KR.properties
+++ 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_ko_KR.properties
@@ -18,7 +18,7 @@
 #
 #
 
-PropertyOutput.Log.CanNotCreateParentFolder=\uBD80\uBAA8 \uD3F4\uB354 
[{0}]\uB97C \uC0DD\uC131\uD558\uC600\uC2B5\uB2C8\uB2E4.
+PropertyOutput.Log.ParentFolderCreated=\uBD80\uBAA8 \uD3F4\uB354 [{0}]\uB97C 
\uC0DD\uC131\uD558\uC600\uC2B5\uB2C8\uB2E4.
 PropertyOutput.Log.ErrorFindingField=\uC785\uB825 
\uC2A4\uD2B8\uB9BC\uC5D0\uC11C \uD544\uB4DC [{0}]\uB97C \uCC3E\uC744 \uC218 
\uC5C6\uC2B5\uB2C8\uB2E4\!
 PropertyOutput.Log.FileAddedResult=\uD30C\uC77C [{0}]\uB97C \uACB0\uACFC 
\uD30C\uC77C\uC774\uB984\uC5D0 \uCD94\uAC00\uD558\uC600\uC2B5\uB2C8\uB2E4
 PropertyOutput.Log.FilenameEmpty=\uD30C\uC77C\uC774\uB984\uC744 
\uC785\uB825\uD558\uC2EC\uC2DC\uC624\!
diff --git 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_pt_BR.properties
 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_pt_BR.properties
index 6184f22951..e49d4d1bf2 100644
--- 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_pt_BR.properties
+++ 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_pt_BR.properties
@@ -17,7 +17,7 @@
 #
 #
 
-PropertyOutput.Log.CanNotCreateParentFolder=Pasta pai [{0}] foi criada.
+PropertyOutput.Log.ParentFolderCreated=Pasta pai [{0}] foi criada.
 PropertyOutput.Log.FileAddedResult=Arquivo [{0}] foi adicionado ao resultado
 PropertyOutput.Log.Key=Chave:{0}
 PropertyOutput.Log.Value=Valor:{0}
diff --git 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_zh_CN.properties
 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_zh_CN.properties
index 0588b1c6ef..57b9282de0 100644
--- 
a/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_zh_CN.properties
+++ 
b/plugins/transforms/propertyoutput/src/main/resources/org/apache/hop/pipeline/transforms/propertyoutput/messages/messages_zh_CN.properties
@@ -18,7 +18,7 @@
 #
 
 PropertyOutput.Description=\u5199\u6570\u636E\u5230\u5C5E\u6027\u6587\u4EF6
-PropertyOutput.Log.CanNotCreateParentFolder=\u5DF2\u521B\u5EFA\u4E0A\u7EA7\u76EE\u5F55
 [{0}]
+PropertyOutput.Log.ParentFolderCreated=\u5DF2\u521B\u5EFA\u4E0A\u7EA7\u76EE\u5F55
 [{0}]
 
PropertyOutput.Log.ErrorFindingField=\u4ECE\u524D\u7F6E\u901A\u9053\u4E2D\u627E\u4E0D\u5230\u5B57\u6BB5
 {0}\!
 PropertyOutput.Log.FileAddedResult=\u6DFB\u52A0\u6587\u4EF6 [{0}] 
\u5230\u8FD4\u56DE\u6587\u4EF6\u5217\u8868
 PropertyOutput.Log.FilenameEmpty=\u8BF7\u6307\u5B9A\u6587\u4EF6\u540D\!
diff --git 
a/plugins/transforms/propertyoutput/src/test/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputIT.java
 
b/plugins/transforms/propertyoutput/src/test/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputIT.java
deleted file mode 100644
index d2eb62f86a..0000000000
--- 
a/plugins/transforms/propertyoutput/src/test/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputIT.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hop.pipeline.transforms.propertyoutput;
-
-import org.apache.hop.core.HopClientEnvironment;
-import org.apache.hop.core.plugins.PluginRegistry;
-import org.apache.hop.core.plugins.TransformPluginType;
-import org.junit.jupiter.api.BeforeEach;
-
-public class PropertyOutputIT {
-
-  @BeforeEach
-  void setUp() throws Exception {
-    HopClientEnvironment.init();
-    PluginRegistry.addPluginType(TransformPluginType.getInstance());
-    PluginRegistry.init();
-  }
-}
diff --git 
a/plugins/transforms/propertyoutput/src/test/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputMetaTest.java
 
b/plugins/transforms/propertyoutput/src/test/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputMetaTest.java
index 53f86e673e..d6deb3b43f 100644
--- 
a/plugins/transforms/propertyoutput/src/test/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputMetaTest.java
+++ 
b/plugins/transforms/propertyoutput/src/test/java/org/apache/hop/pipeline/transforms/propertyoutput/PropertyOutputMetaTest.java
@@ -17,46 +17,54 @@
 
 package org.apache.hop.pipeline.transforms.propertyoutput;
 
-import java.util.Arrays;
-import java.util.List;
-import org.apache.hop.core.exception.HopException;
-import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
-import org.apache.hop.pipeline.transforms.loadsave.LoadSaveTester;
+import org.apache.commons.lang.StringUtils;
+import org.apache.hop.pipeline.transform.TransformSerializationTestUtil;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 
 class PropertyOutputMetaTest {
-  @RegisterExtension
-  static RestoreHopEngineEnvironmentExtension env = new 
RestoreHopEngineEnvironmentExtension();
-
   @Test
-  void testSerialization() throws HopException {
-    List<String> attributes =
-        Arrays.asList(
-            "KeyField",
-            "ValueField",
-            "Comment",
-            "FileNameInField",
-            "FileNameField",
-            "FileName",
-            "Extension",
-            "TransformNrInFilename",
-            //
-            // Note - "partNrInFilename" not included above because while it 
seems to be
-            // serialized/deserialized in the meta,
-            // there are no getters/setters and it's a private variable. Also, 
it's not included in
-            // the dialog. So it is
-            // always serialized/deserialized as "false" (N).
-            // MB - 5/2016
-            "DateInFilename",
-            "TimeInFilename",
-            "CreateParentFolder",
-            "AddToResult",
-            "Append");
+  void testXmlRoundTrip() throws Exception {
+    PropertyOutputMeta meta =
+        TransformSerializationTestUtil.testSerialization(
+            "/properties-output.xml", PropertyOutputMeta.class);
+
+    Assertions.assertEquals("key-field", meta.getKeyField());
+    Assertions.assertEquals("value-field", meta.getValueField());
+    Assertions.assertEquals("This is a comment", meta.getComment());
+    Assertions.assertEquals("filename-field", meta.getFileNameField());
+    Assertions.assertFalse(meta.isFileNameInField());
+    Assertions.assertEquals("filename", meta.getFileDetails().getFileName());
+    Assertions.assertEquals("properties", 
meta.getFileDetails().getExtension());
+    Assertions.assertTrue(meta.getFileDetails().isTransformNrInFilename());
+    Assertions.assertFalse(meta.getFileDetails().isPartitionIdInFilename());
+    Assertions.assertTrue(meta.getFileDetails().isDateInFilename());
+    Assertions.assertTrue(meta.getFileDetails().isTimeInFilename());
+    Assertions.assertTrue(meta.getFileDetails().isCreateParentFolder());
+    Assertions.assertTrue(meta.getFileDetails().isAddToResult());
+    Assertions.assertTrue(meta.getFileDetails().isAppending());
+  }
 
-    LoadSaveTester<PropertyOutputMeta> tester =
-        new LoadSaveTester<>(PropertyOutputMeta.class, attributes);
+  @Test
+  void testLegacyXmlRoundTrip() throws Exception {
+    PropertyOutputMeta meta =
+        TransformSerializationTestUtil.testSerialization(
+            "/properties-output-legacy.xml", PropertyOutputMeta.class);
 
-    tester.testSerialization();
+    Assertions.assertEquals("propertyName", meta.getKeyField());
+    Assertions.assertEquals("propertyValue", meta.getValueField());
+    Assertions.assertTrue(StringUtils.isEmpty(meta.getComment()));
+    Assertions.assertTrue(StringUtils.isEmpty(meta.getFileNameField()));
+    Assertions.assertFalse(meta.isFileNameInField());
+    Assertions.assertEquals(
+        "${Internal.Entry.Current.Directory}\\propertyOutput", 
meta.getFileDetails().getFileName());
+    Assertions.assertEquals("properties", 
meta.getFileDetails().getExtension());
+    Assertions.assertFalse(meta.getFileDetails().isTransformNrInFilename());
+    Assertions.assertFalse(meta.getFileDetails().isPartitionIdInFilename());
+    Assertions.assertFalse(meta.getFileDetails().isDateInFilename());
+    Assertions.assertFalse(meta.getFileDetails().isTimeInFilename());
+    Assertions.assertFalse(meta.getFileDetails().isCreateParentFolder());
+    Assertions.assertFalse(meta.getFileDetails().isAddToResult());
+    Assertions.assertTrue(meta.getFileDetails().isAppending());
   }
 }
diff --git 
a/plugins/transforms/propertyoutput/src/test/resources/properties-output-legacy.xml
 
b/plugins/transforms/propertyoutput/src/test/resources/properties-output-legacy.xml
new file mode 100644
index 0000000000..a49a5fe345
--- /dev/null
+++ 
b/plugins/transforms/propertyoutput/src/test/resources/properties-output-legacy.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  -->
+
+<transform>
+    <name>Properties Output</name>
+    <type>PropertyOutput</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+        <method>none</method>
+        <schema_name/>
+    </partitioning>
+    <keyfield>propertyName</keyfield>
+    <valuefield>propertyValue</valuefield>
+    <comment/>
+    <fileNameInField>N</fileNameInField>
+    <fileNameField/>
+    <file>
+        <name>${Internal.Entry.Current.Directory}\propertyOutput</name>
+        <extention>properties</extention>
+        <split>N</split>
+        <haspartno>N</haspartno>
+        <add_date>N</add_date>
+        <add_time>N</add_time>
+        <create_parent_folder>N</create_parent_folder>
+        <addtoresult>N</addtoresult>
+        <append>Y</append>
+    </file>
+    <input>
+    </input>
+    <output>
+    </output>
+
+    <GUI>
+        <xloc>336</xloc>
+        <yloc>144</yloc>
+        <draw>Y</draw>
+    </GUI>
+</transform>
\ No newline at end of file
diff --git 
a/plugins/transforms/propertyoutput/src/test/resources/properties-output.xml 
b/plugins/transforms/propertyoutput/src/test/resources/properties-output.xml
new file mode 100644
index 0000000000..b34130fa3c
--- /dev/null
+++ b/plugins/transforms/propertyoutput/src/test/resources/properties-output.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  -->
+
+<transform>
+    <keyfield>key-field</keyfield>
+    <valuefield>value-field</valuefield>
+    <comment>This is a comment</comment>
+    <fileNameInField>N</fileNameInField>
+    <fileNameField>filename-field</fileNameField>
+    <file>
+        <name>filename</name>
+        <extention>properties</extention>
+        <split>Y</split>
+        <haspartno>N</haspartno>
+        <add_date>Y</add_date>
+        <add_time>Y</add_time>
+        <create_parent_folder>Y</create_parent_folder>
+        <addtoresult>Y</addtoresult>
+        <append>Y</append>
+    </file>
+</transform>
diff --git 
a/plugins/transforms/propertyoutput/src/test/resources/propertyOutput.hpl 
b/plugins/transforms/propertyoutput/src/test/resources/propertyOutput.hpl
deleted file mode 100644
index 4192a55310..0000000000
--- a/plugins/transforms/propertyoutput/src/test/resources/propertyOutput.hpl
+++ /dev/null
@@ -1,549 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-Licensed to the Apache Software Foundation (ASF) under one or more
-contributor license agreements.  See the NOTICE file distributed with
-this work for additional information regarding copyright ownership.
-The ASF licenses this file to You under the Apache License, Version 2.0
-(the "License"); you may not use this file except in compliance with
-the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
--->
-<pipeline>
-  <info>
-    <name>propertyOutput</name>
-    <description/>
-    <extended_description/>
-    <trans_version/>
-    <trans_type>Normal</trans_type>
-    <directory>/</directory>
-    <parameters>
-    </parameters>
-    <log>
-      <pipeline-log-table>
-        <connection/>
-        <schema/>
-        <table/>
-        <size_limit_lines/>
-        <interval/>
-        <timeout_days/>
-        <field>
-          <id>ID_BATCH</id>
-          <enabled>Y</enabled>
-          <name>ID_BATCH</name>
-        </field>
-        <field>
-          <id>CHANNEL_ID</id>
-          <enabled>Y</enabled>
-          <name>CHANNEL_ID</name>
-        </field>
-        <field>
-          <id>TRANSNAME</id>
-          <enabled>Y</enabled>
-          <name>TRANSNAME</name>
-        </field>
-        <field>
-          <id>STATUS</id>
-          <enabled>Y</enabled>
-          <name>STATUS</name>
-        </field>
-        <field>
-          <id>LINES_READ</id>
-          <enabled>Y</enabled>
-          <name>LINES_READ</name>
-          <subject/>
-        </field>
-        <field>
-          <id>LINES_WRITTEN</id>
-          <enabled>Y</enabled>
-          <name>LINES_WRITTEN</name>
-          <subject/>
-        </field>
-        <field>
-          <id>LINES_UPDATED</id>
-          <enabled>Y</enabled>
-          <name>LINES_UPDATED</name>
-          <subject/>
-        </field>
-        <field>
-          <id>LINES_INPUT</id>
-          <enabled>Y</enabled>
-          <name>LINES_INPUT</name>
-          <subject/>
-        </field>
-        <field>
-          <id>LINES_OUTPUT</id>
-          <enabled>Y</enabled>
-          <name>LINES_OUTPUT</name>
-          <subject/>
-        </field>
-        <field>
-          <id>LINES_REJECTED</id>
-          <enabled>Y</enabled>
-          <name>LINES_REJECTED</name>
-          <subject/>
-        </field>
-        <field>
-          <id>ERRORS</id>
-          <enabled>Y</enabled>
-          <name>ERRORS</name>
-        </field>
-        <field>
-          <id>STARTDATE</id>
-          <enabled>Y</enabled>
-          <name>STARTDATE</name>
-        </field>
-        <field>
-          <id>ENDDATE</id>
-          <enabled>Y</enabled>
-          <name>ENDDATE</name>
-        </field>
-        <field>
-          <id>LOGDATE</id>
-          <enabled>Y</enabled>
-          <name>LOGDATE</name>
-        </field>
-        <field>
-          <id>DEPDATE</id>
-          <enabled>Y</enabled>
-          <name>DEPDATE</name>
-        </field>
-        <field>
-          <id>REPLAYDATE</id>
-          <enabled>Y</enabled>
-          <name>REPLAYDATE</name>
-        </field>
-        <field>
-          <id>LOG_FIELD</id>
-          <enabled>Y</enabled>
-          <name>LOG_FIELD</name>
-        </field>
-        <field>
-          <id>EXECUTING_SERVER</id>
-          <enabled>N</enabled>
-          <name>EXECUTING_SERVER</name>
-        </field>
-        <field>
-          <id>EXECUTING_USER</id>
-          <enabled>N</enabled>
-          <name>EXECUTING_USER</name>
-        </field>
-        <field>
-          <id>CLIENT</id>
-          <enabled>N</enabled>
-          <name>CLIENT</name>
-        </field>
-      </pipeline-log-table>
-      <perf-log-table>
-        <connection/>
-        <schema/>
-        <table/>
-        <interval/>
-        <timeout_days/>
-        <field>
-          <id>ID_BATCH</id>
-          <enabled>Y</enabled>
-          <name>ID_BATCH</name>
-        </field>
-        <field>
-          <id>SEQ_NR</id>
-          <enabled>Y</enabled>
-          <name>SEQ_NR</name>
-        </field>
-        <field>
-          <id>LOGDATE</id>
-          <enabled>Y</enabled>
-          <name>LOGDATE</name>
-        </field>
-        <field>
-          <id>TRANSNAME</id>
-          <enabled>Y</enabled>
-          <name>TRANSNAME</name>
-        </field>
-        <field>
-          <id>STEPNAME</id>
-          <enabled>Y</enabled>
-          <name>STEPNAME</name>
-        </field>
-        <field>
-          <id>STEP_COPY</id>
-          <enabled>Y</enabled>
-          <name>STEP_COPY</name>
-        </field>
-        <field>
-          <id>LINES_READ</id>
-          <enabled>Y</enabled>
-          <name>LINES_READ</name>
-        </field>
-        <field>
-          <id>LINES_WRITTEN</id>
-          <enabled>Y</enabled>
-          <name>LINES_WRITTEN</name>
-        </field>
-        <field>
-          <id>LINES_UPDATED</id>
-          <enabled>Y</enabled>
-          <name>LINES_UPDATED</name>
-        </field>
-        <field>
-          <id>LINES_INPUT</id>
-          <enabled>Y</enabled>
-          <name>LINES_INPUT</name>
-        </field>
-        <field>
-          <id>LINES_OUTPUT</id>
-          <enabled>Y</enabled>
-          <name>LINES_OUTPUT</name>
-        </field>
-        <field>
-          <id>LINES_REJECTED</id>
-          <enabled>Y</enabled>
-          <name>LINES_REJECTED</name>
-        </field>
-        <field>
-          <id>ERRORS</id>
-          <enabled>Y</enabled>
-          <name>ERRORS</name>
-        </field>
-        <field>
-          <id>INPUT_BUFFER_ROWS</id>
-          <enabled>Y</enabled>
-          <name>INPUT_BUFFER_ROWS</name>
-        </field>
-        <field>
-          <id>OUTPUT_BUFFER_ROWS</id>
-          <enabled>Y</enabled>
-          <name>OUTPUT_BUFFER_ROWS</name>
-        </field>
-      </perf-log-table>
-      <channel-log-table>
-        <connection/>
-        <schema/>
-        <table/>
-        <timeout_days/>
-        <field>
-          <id>ID_BATCH</id>
-          <enabled>Y</enabled>
-          <name>ID_BATCH</name>
-        </field>
-        <field>
-          <id>CHANNEL_ID</id>
-          <enabled>Y</enabled>
-          <name>CHANNEL_ID</name>
-        </field>
-        <field>
-          <id>LOG_DATE</id>
-          <enabled>Y</enabled>
-          <name>LOG_DATE</name>
-        </field>
-        <field>
-          <id>LOGGING_OBJECT_TYPE</id>
-          <enabled>Y</enabled>
-          <name>LOGGING_OBJECT_TYPE</name>
-        </field>
-        <field>
-          <id>OBJECT_NAME</id>
-          <enabled>Y</enabled>
-          <name>OBJECT_NAME</name>
-        </field>
-        <field>
-          <id>OBJECT_COPY</id>
-          <enabled>Y</enabled>
-          <name>OBJECT_COPY</name>
-        </field>
-        <field>
-          <id>REPOSITORY_DIRECTORY</id>
-          <enabled>Y</enabled>
-          <name>REPOSITORY_DIRECTORY</name>
-        </field>
-        <field>
-          <id>FILENAME</id>
-          <enabled>Y</enabled>
-          <name>FILENAME</name>
-        </field>
-        <field>
-          <id>OBJECT_ID</id>
-          <enabled>Y</enabled>
-          <name>OBJECT_ID</name>
-        </field>
-        <field>
-          <id>OBJECT_REVISION</id>
-          <enabled>Y</enabled>
-          <name>OBJECT_REVISION</name>
-        </field>
-        <field>
-          <id>PARENT_CHANNEL_ID</id>
-          <enabled>Y</enabled>
-          <name>PARENT_CHANNEL_ID</name>
-        </field>
-        <field>
-          <id>ROOT_CHANNEL_ID</id>
-          <enabled>Y</enabled>
-          <name>ROOT_CHANNEL_ID</name>
-        </field>
-      </channel-log-table>
-      <transform-log-table>
-        <connection/>
-        <schema/>
-        <table/>
-        <timeout_days/>
-        <field>
-          <id>ID_BATCH</id>
-          <enabled>Y</enabled>
-          <name>ID_BATCH</name>
-        </field>
-        <field>
-          <id>CHANNEL_ID</id>
-          <enabled>Y</enabled>
-          <name>CHANNEL_ID</name>
-        </field>
-        <field>
-          <id>LOG_DATE</id>
-          <enabled>Y</enabled>
-          <name>LOG_DATE</name>
-        </field>
-        <field>
-          <id>TRANSNAME</id>
-          <enabled>Y</enabled>
-          <name>TRANSNAME</name>
-        </field>
-        <field>
-          <id>STEPNAME</id>
-          <enabled>Y</enabled>
-          <name>STEPNAME</name>
-        </field>
-        <field>
-          <id>STEP_COPY</id>
-          <enabled>Y</enabled>
-          <name>STEP_COPY</name>
-        </field>
-        <field>
-          <id>LINES_READ</id>
-          <enabled>Y</enabled>
-          <name>LINES_READ</name>
-        </field>
-        <field>
-          <id>LINES_WRITTEN</id>
-          <enabled>Y</enabled>
-          <name>LINES_WRITTEN</name>
-        </field>
-        <field>
-          <id>LINES_UPDATED</id>
-          <enabled>Y</enabled>
-          <name>LINES_UPDATED</name>
-        </field>
-        <field>
-          <id>LINES_INPUT</id>
-          <enabled>Y</enabled>
-          <name>LINES_INPUT</name>
-        </field>
-        <field>
-          <id>LINES_OUTPUT</id>
-          <enabled>Y</enabled>
-          <name>LINES_OUTPUT</name>
-        </field>
-        <field>
-          <id>LINES_REJECTED</id>
-          <enabled>Y</enabled>
-          <name>LINES_REJECTED</name>
-        </field>
-        <field>
-          <id>ERRORS</id>
-          <enabled>Y</enabled>
-          <name>ERRORS</name>
-        </field>
-        <field>
-          <id>LOG_FIELD</id>
-          <enabled>N</enabled>
-          <name>LOG_FIELD</name>
-        </field>
-      </transform-log-table>
-      <metrics-log-table>
-        <connection/>
-        <schema/>
-        <table/>
-        <timeout_days/>
-        <field>
-          <id>ID_BATCH</id>
-          <enabled>Y</enabled>
-          <name>ID_BATCH</name>
-        </field>
-        <field>
-          <id>CHANNEL_ID</id>
-          <enabled>Y</enabled>
-          <name>CHANNEL_ID</name>
-        </field>
-        <field>
-          <id>LOG_DATE</id>
-          <enabled>Y</enabled>
-          <name>LOG_DATE</name>
-        </field>
-        <field>
-          <id>METRICS_DATE</id>
-          <enabled>Y</enabled>
-          <name>METRICS_DATE</name>
-        </field>
-        <field>
-          <id>METRICS_CODE</id>
-          <enabled>Y</enabled>
-          <name>METRICS_CODE</name>
-        </field>
-        <field>
-          <id>METRICS_DESCRIPTION</id>
-          <enabled>Y</enabled>
-          <name>METRICS_DESCRIPTION</name>
-        </field>
-        <field>
-          <id>METRICS_SUBJECT</id>
-          <enabled>Y</enabled>
-          <name>METRICS_SUBJECT</name>
-        </field>
-        <field>
-          <id>METRICS_TYPE</id>
-          <enabled>Y</enabled>
-          <name>METRICS_TYPE</name>
-        </field>
-        <field>
-          <id>METRICS_VALUE</id>
-          <enabled>Y</enabled>
-          <name>METRICS_VALUE</name>
-        </field>
-      </metrics-log-table>
-    </log>
-    <maxdate>
-      <connection/>
-      <table/>
-      <field/>
-      <offset>0.0</offset>
-      <maxdiff>0.0</maxdiff>
-    </maxdate>
-    <size_rowset>10000</size_rowset>
-    <sleep_time_empty>50</sleep_time_empty>
-    <sleep_time_full>50</sleep_time_full>
-    <unique_connections>N</unique_connections>
-    <feedback_shown>Y</feedback_shown>
-    <feedback_size>50000</feedback_size>
-    <using_thread_priorities>Y</using_thread_priorities>
-    <shared_objects_file/>
-    <capture_transform_performance>N</capture_transform_performance>
-    
<transform_performance_capturing_delay>1000</transform_performance_capturing_delay>
-    
<transform_performance_capturing_size_limit>100</transform_performance_capturing_size_limit>
-    <dependencies>
-    </dependencies>
-    <created_user>-</created_user>
-    <created_date>2018/02/21 19:18:45.746</created_date>
-    <modified_user>-</modified_user>
-    <modified_date>2018/02/21 19:18:45.746</modified_date>
-    <key_for_session_key>H4sIAAAAAAAAAAMAAAAAAAAAAAA=</key_for_session_key>
-    <is_key_private>N</is_key_private>
-  </info>
-  <notepads>
-  </notepads>
-  <order>
-    <hop>
-      <from>Data Grid</from>
-      <to>Properties Output</to>
-      <enabled>Y</enabled>
-    </hop>
-  </order>
-  <transform>
-    <name>Data Grid</name>
-    <type>DataGrid</type>
-    <description/>
-    <distribute>Y</distribute>
-    <custom_distribution/>
-    <copies>1</copies>
-    <partitioning>
-      <method>none</method>
-      <schema_name/>
-    </partitioning>
-    <fields>
-      <field>
-        <name>propertyName</name>
-        <type>String</type>
-        <format/>
-        <currency/>
-        <decimal/>
-        <group/>
-        <length>-1</length>
-        <precision>-1</precision>
-        <set_empty_string>N</set_empty_string>
-      </field>
-      <field>
-        <name>propertyValue</name>
-        <type>String</type>
-        <format/>
-        <currency/>
-        <decimal/>
-        <group/>
-        <length>-1</length>
-        <precision>-1</precision>
-        <set_empty_string>N</set_empty_string>
-      </field>
-    </fields>
-    <data>
-      <line>
-        <item>propertyName</item>
-        <item>propertyValue</item>
-      </line>
-    </data>
-<input>
-      </input>
-      <output>
-      </output>
-
-    <GUI>
-      <xloc>112</xloc>
-      <yloc>144</yloc>
-      <draw>Y</draw>
-    </GUI>
-  </transform>
-  <transform>
-    <name>Properties Output</name>
-    <type>PropertyOutput</type>
-    <description/>
-    <distribute>Y</distribute>
-    <custom_distribution/>
-    <copies>1</copies>
-    <partitioning>
-      <method>none</method>
-      <schema_name/>
-    </partitioning>
-    <keyfield>propertyName</keyfield>
-    <valuefield>propertyValue</valuefield>
-    <comment/>
-    <fileNameInField>N</fileNameInField>
-    <fileNameField/>
-    <file>
-      <name>${Internal.Entry.Current.Directory}\propertyOutput</name>
-      <extention>properties</extention>
-      <split>N</split>
-      <haspartno>N</haspartno>
-      <add_date>N</add_date>
-      <add_time>N</add_time>
-      <create_parent_folder>N</create_parent_folder>
-      <addtoresult>N</addtoresult>
-      <append>Y</append>
-    </file>
-<input>
-      </input>
-      <output>
-      </output>
-
-    <GUI>
-      <xloc>336</xloc>
-      <yloc>144</yloc>
-      <draw>Y</draw>
-    </GUI>
-  </transform>
-  <transform_error_handling>
-  </transform_error_handling>
-</pipeline>

Reply via email to