Skip to content

How To: Write MSPDI files

Since Microsoft Project 2002, Microsoft Project has been able to read and write an XML-based data interchange format called MSPDI.

Writing MSPDI files

The sample code below illustrates how to write data to an MSPDI file.

package org.mpxj.howto.write;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.writer.FileFormat;
import net.sf.mpxj.writer.UniversalProjectWriter;

public class MSPDI
{
    public void write(ProjectFile project, String fileName) throws Exception
    {
        new UniversalProjectWriter(FileFormat.MSPDI).write(project, fileName);
    }
}
using MPXJ.Net;

namespace MPXJ.Samples.HowToWrite;

public class MSPDI
{
    public void Write(ProjectFile project, string fileName)
    {
        new UniversalProjectWriter(FileFormat.MSPDI).Write(project, fileName);
    }
}

Using MSPDIWriter

If required, the MSPDIWriter class can be used directly, which provides access to additional options, as described below.

Microsoft Project Compatible Output

Microsoft Project has a non-standard way of representing negative duration values (it should have a minus sign as a prefix at the start of the XSD duration expression rather than embedded in it).

Originally MPXJ read and wrote correctly formatted XSD duration values, but unfortunately this meant that Project would not read these values correctly, and MPXJ would not be able to consume these values correctly from an MSPDI file written by Project. MPXJ has been updated so that it reads and writes the form of these duration values understood by Project, but this does mean that if you were previously expecting to be able to parse valid XSD duration values from output generated by MPXJ, that will no longer be the case.

To provide backward compatibility the MicrosoftProjectCompatibleOutput flag has been introduced. This defaults to true so MSPDI files containing negative durations written by MPXJ can be read by Project. If you need to produce correctly formatted XSD durations for consumption by applications other than Project you can set this flag to false:

package org.mpxj.howto.write;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.mspdi.MSPDIWriter;

public class MSPDICompatibleOutput
{
    public void write(ProjectFile project, String fileName) throws Exception
    {
        MSPDIWriter writer = new MSPDIWriter();
        writer.setMicrosoftProjectCompatibleOutput(false);
        writer.write(project, fileName);
    }
}
using MPXJ.Net;

namespace MPXJ.Samples.HowToWrite;

public class MSPDICompatibleOutput
{
    public void Write(ProjectFile project, string fileName)
    {
        var writer = new MSPDIWriter();
        writer.MicrosoftProjectCompatibleOutput = false;
        writer.Write(project, fileName);
    }
}

Save Version

The MSPDI file contains a SaveVersion attribute which indicates the version of Microsoft Project used to save the file. The value of SaveVersion is defined by the net.sf.mpxj.mspdi.SaveVersion enum, which provides the following values:

Project2002
Project2003
Project2007
Project2010
Project2013
Project2016

By default MSPDIWriter sets the SaveVersion value to Project2016. The only functional difference this setting makes when writing MSPDI files is that the format of calendar exceptions changed in Project 2003 and onwards. MPXJ will always write calendar exceptions using the original Project 2002 format, and if the SaveVersion is set to Project2003 or later it will also write the new format data as well.

Here's an example of the SaveVersion attribute being set to ensure that only the older style of calendar exceptions is written to the MSPDI file:

package org.mpxj.howto.write;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.mspdi.MSPDIWriter;
import net.sf.mpxj.mspdi.SaveVersion;

public class MSPDISaveVersion
{
    public void write(ProjectFile project, String fileName) throws Exception
    {
        MSPDIWriter writer = new MSPDIWriter();
        writer.setSaveVersion(SaveVersion.Project2002);
        writer.write(project, fileName);
    }
}
using MPXJ.Net;

namespace MPXJ.Samples.HowToWrite;

public class MSPDISaveVersion
{
    public void Write(ProjectFile project, string fileName)
    {
        var writer = new MSPDIWriter();
        writer.SaveVersion = SaveVersion.Project2002;
        writer.Write(project, fileName);
    }
}

Timephased Data

By default MSPDIWriter does not write timephased data to an MSPDI file. To enable writing timephased data, you can call the setWriteTimephasedData method.

When this setting is enabled, the default behaviour is for the timephased data is broken down into days when written to the file. If it better suits your use case (or you need a more compact file) you can choose to write an aggregated form of the timephased data by calling the setSplitTimephasedAsDays method and passing false. The difference between the two formats is that if for example you have a 10 day block with 8 hours work per day, this can either be represented as 10 entries in the file each for a single day with a value of 8 hours, or a single entry for a 10 day range with a value of 80 hours. Although the latter case is more compact, if you are consuming the MSPDI timephased data yourself you will need to differentiate between working and non-working days in order to break the single block down into smaller ranges. The default day-by-day format MPXJ writes does this for you automatically.

In the first example below we're enabling timephased data, and using the default day-by-dat breakdown:

package org.mpxj.howto.write;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.mspdi.MSPDIWriter;

public class MSPDITimephased
{
    public void write(ProjectFile project, String fileName) throws Exception
    {
        MSPDIWriter writer = new MSPDIWriter();
        writer.setWriteTimephasedData(true);
        writer.write(project, fileName);
    }
}
using MPXJ.Net;

namespace MPXJ.Samples.HowToWrite;

public class MSPDITimephased
{
    public void Write(ProjectFile project, string fileName)
    {
        var writer = new MSPDIWriter();
        writer.WriteTimephasedData = true;
        writer.Write(project, fileName);
    }
}

In this second example we're overriding the default behaviour as asking MPXJ to write an aggregated form of the timephased data:

package org.mpxj.howto.write;

import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.mspdi.MSPDIWriter;

public class MSPDITimephasedAggregate
{
    public void write(ProjectFile project, String fileName) throws Exception
    {
        MSPDIWriter writer = new MSPDIWriter();
        writer.setWriteTimephasedData(true);
        writer.setSplitTimephasedAsDays(false);
        writer.write(project, fileName);
    }
}
using MPXJ.Net;

namespace MPXJ.Samples.HowToWrite;

public class MSPDITimephasedAggregate
{
    public void Write(ProjectFile project, string fileName)
    {
        var writer = new MSPDIWriter();
        writer.WriteTimephasedData = true;
        writer.SplitTimephasedAsDays = true;
        writer.Write(project, fileName);
    }
}