Skip to content

How To: Read XER files

The XER file format has long been read and written by Primavera P6. Although an XML file format (PMXML) is now also supported, the XER file format is still widely used.

Reading XER files

The simplest way to read an XER file is to use the UniversalProjectReader:

package org.mpxj.howto.read;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.reader.UniversalProjectReader;

public class XER
{
    public void read() throws Exception
    {
        UniversalProjectReader reader = new UniversalProjectReader();
        ProjectFile project = reader.read("my-sample.xer");
    }
}
using MPXJ.Net;

namespace MPXJ.Samples.HowToRead;

public class XER
{
    public void Read()
    {
        var reader = new UniversalProjectReader();
        var project = reader.Read("my-sample.xer");
    }
}

Using PrimaveraXERFileReader

You can work directly with the PrimaveraXERFileReader class by replacing UniversalProjectReader with PrimaveraXERFileReader. This provides access to additional options, as described below.

Ignore Errors

By default P6 ignores records it can't successfully read from an XER file. MPXJ takes the same approach, and in most cases if it doesn't receive the data it expects for a particular record it will ignore the problematic item.

This behavior is controlled using the setIgnoreErrors method. The example below illustrates how we can force the PrimaveraXERFileReader to report errors encountered when reading a file:

package org.mpxj.howto.read;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.primavera.PrimaveraXERFileReader;

public class XERIgnoreErrors
{
    public void read() throws Exception
    {
        PrimaveraXERFileReader reader = new PrimaveraXERFileReader();
        reader.setIgnoreErrors(false);
        ProjectFile project = reader.read("my-sample.xer");
    }
}

Note that if errors are ignored when reading a file, the ignored errors are available by using the ProjectFile.getIgnoredErrors() method.

Charset

By default MPXJ assumes that XER files use the Windows-1252 Charset. The UniversalProjectReader understands Unicode Byte Order Marks (BOM) and will adjust the Charset appropriately if a BOM is present. If you have an XER file with an unusual encoding, you can manually set the Charset used by the reader.

package org.mpxj.howto.read;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.primavera.PrimaveraXERFileReader;

import java.nio.charset.Charset;

public class XERCharset
{
    public void read() throws Exception
    {
        PrimaveraXERFileReader reader = new PrimaveraXERFileReader();
        reader.setCharset(Charset.forName("GB2312"));
        ProjectFile project = reader.read("my-sample.xer");
    }
}

Multiple Projects

An XER file can contain multiple projects. By default MPXJ reads the first project it finds in the file which has been marked as the "exported" project, otherwise it will simply read the first project it finds. You can however use MPXJ to list the projects contained in an XER file, as shown below:

package org.mpxj.howto.read;

import net.sf.mpxj.primavera.PrimaveraXERFileReader;

import java.io.FileInputStream;
import java.util.Map;

public class XERListProjects
{
    public void read() throws Exception
    {
        PrimaveraXERFileReader reader = new PrimaveraXERFileReader();
        FileInputStream is = new FileInputStream("my-sample.xer");
        Map<Integer, String> projects = reader.listProjects(is);
        System.out.println("ID\tName");
        for (Map.Entry<Integer, String> entry : projects.entrySet())
        {
            System.out.println(entry.getKey()+"\t"+entry.getValue());
        }
    }
}
The call to listProjects returns a Map whose key is the project ID, and the values are project short names.

Once you have decided which of these projects you want to work with, you can call setProjectID to tell the reader which project to open, as shown below.

package org.mpxj.howto.read;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.primavera.PrimaveraXERFileReader;

public class XERProjectID
{
    public void read() throws Exception
    {
        PrimaveraXERFileReader reader = new PrimaveraXERFileReader();
        reader.setProjectID(123);
        ProjectFile file = reader.read("my-sample.xer");
    }
}

Alternatively you can ask MPXJ to read all of the projects contained in the file:

package org.mpxj.howto.read;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.primavera.PrimaveraXERFileReader;

import java.util.List;

public class XERReadAll
{
   public void read() throws Exception
   {
      PrimaveraXERFileReader reader = new PrimaveraXERFileReader();
      List<ProjectFile> files = reader.readAll("my-sample.xer");
   }
}

The call to the readAll method returns a list of ProjectFile instances corresponding to the projects in the XER file.

An XER file can contain multiple projects with relations between activities which span those projects. By default these cross-project relations are ignored. However, if you set the linkCrossProjectRelations reader attribute to true, MPXJ will attempt to link these relations across projects:

package org.mpxj.howto.read;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.primavera.PrimaveraXERFileReader;

import java.util.List;

public class XERLinkCrossProject
{
   public void read() throws Exception
   {
      PrimaveraXERFileReader reader = new PrimaveraXERFileReader();
      reader.setLinkCrossProjectRelations(true);
      List<ProjectFile> files = reader.readAll("my-sample.xer");
   }
}

Activity WBS

In the original implementation of the XER file handling code, MPXJ would assign each task representing a Primavera Activity its own distinct WBS value. This does not match Primavera's behaviour where all of a WBS element's child activities will have the same WBS value as the parent WBS element. MPXJ's default behaviour now matches Primavera, but should you wish to you can revert to the original behaviour by calling the setMatchPrimaveraWBS as shown below.

package org.mpxj.howto.read;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.primavera.PrimaveraXERFileReader;

public class XERMatchWbs
{
    public void read() throws Exception
    {
        PrimaveraXERFileReader reader = new PrimaveraXERFileReader();
        reader.setMatchPrimaveraWBS(false);
        ProjectFile file = reader.read("my-sample.xer");
    }
}

WBS is Full Path

Currently the WBS attribute of summary tasks (WBS entities in P6) will be a dot separated hierarchy of all of the parent WBS attributes. In this example, root.wbs1.wbs2 is the WBS attribute for wbs2 which has the parents root and wbs1. To disable this behaviour, and simply record the code for the current WBS entry (in the example above wbs2) call the setWbsIsFullPath method, passing in false, as illustrated below.

package org.mpxj.howto.read;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.primavera.PrimaveraXERFileReader;

public class XERWbsFullPath
{
    public void read() throws Exception
    {
        PrimaveraXERFileReader reader = new PrimaveraXERFileReader();
        reader.setWbsIsFullPath(false);
        ProjectFile file = reader.read("my-sample.xer");
    }
}

Reading Additional Attributes

A data-driven approach is used to extract the attributes used by MPXJ from the XER file. You can if you wish change the way attributes are read from the file, or add support for additional attributes. This assumes that you know the column name of the attributes you want to work with in the XER file. To make changes you will need to retrieve the maps which define which MPXJ attributes are used to store which columns from the XER file:

package org.mpxj.howto.read;

import net.sf.mpxj.FieldType;
import net.sf.mpxj.primavera.PrimaveraXERFileReader;

import java.util.Map;

public class XERAttributeMaps
{
    public void read() throws Exception
    {
        PrimaveraXERFileReader reader = new PrimaveraXERFileReader();
        Map<FieldType, String> resourceFieldMap = reader.getResourceFieldMap();
        Map<FieldType, String> wbsFieldMap = reader.getWbsFieldMap();
        Map<FieldType, String> activityFieldMap = reader.getActivityFieldMap();
        Map<FieldType, String> assignmentFieldMap = reader.getAssignmentFieldMap();
    }
}

These maps will contain the default mapping between columns and MPXJ attributes. You can modify these existing mappings, or add new ones, for example:

package org.mpxj.howto.read;

import net.sf.mpxj.FieldType;
import net.sf.mpxj.TaskField;
import net.sf.mpxj.primavera.PrimaveraXERFileReader;

import java.util.Map;

public class XERAttributeConfig
{
    public void read() throws Exception
    {
        PrimaveraXERFileReader reader = new PrimaveraXERFileReader();
        Map<FieldType, String> activityFieldMap = reader.getActivityFieldMap();

        //
        // Store rsrc_id in NUMBER1
        //
        activityFieldMap.put(TaskField.NUMBER1, "rsrc_id");

        //
        // Read an Activity column called an_example_field and store it in TEXT10
        //
        activityFieldMap.put(TaskField.TEXT10, "an_example_field");
    }
}

When reading new columns from the XER file, if these columns have a type other than String, it is important to register the type of the column to ensure that it is converted correctly. You will also need to ensure that the MPXJ attribute you are writing this new value to can receive the data type you are assigning to it (for example, you must store a date in a date attribute, you can't store a date in an integer attribute).

For example, if we are reading an integer column called an_example_id and store it in the NUMBER2 attribute, we will need to take the following steps:

package org.mpxj.howto.read;

import net.sf.mpxj.DataType;
import net.sf.mpxj.FieldType;
import net.sf.mpxj.TaskField;
import net.sf.mpxj.primavera.PrimaveraXERFileReader;

import java.util.Map;

public class XERRegisterType
{
    public void read() throws Exception
    {
        PrimaveraXERFileReader reader = new PrimaveraXERFileReader();
        Map<String, DataType> fieldTypeMap = reader.getFieldTypeMap();
        fieldTypeMap.put("an_example_id", DataType.INTEGER);
        Map<FieldType, String> activityFieldMap = reader.getActivityFieldMap();
        activityFieldMap.put(TaskField.NUMBER2, "an_example_id");
    }
}