Learning Objectives
In this tutorial, you’ll learn how to add custom, non-existing document properties to MS Project files using Aspose.Tasks Cloud API. By the end of this guide, you’ll be able to:
- Create custom document properties with unique names
- Understand the differences between standard and custom properties
- Implement custom property creation in various programming languages
- Verify and utilize your custom properties
Prerequisites
Before starting this tutorial, ensure you have:
- An Aspose Cloud account with an active subscription or free trial
- Your Client ID and Client Secret from the Aspose Cloud Dashboard
- Basic understanding of REST APIs and your preferred programming language
- An MS Project file uploaded to your Aspose Cloud Storage
- Completed the previous tutorials on retrieving and creating document properties
The Power of Custom Document Properties
Custom document properties extend the metadata capabilities of MS Project files beyond the standard set of properties. They provide several advantages:
- Business-Specific Metadata: Add organization-specific information to project files
- Workflow Integration: Store process status, approval flags, or routing information
- Custom Categorization: Implement tagging systems specific to your project management needs
- Extended Search Capabilities: Add searchable metadata that isn’t covered by standard properties
- Application Integration: Store identifiers or references to other systems
Tutorial Steps
Step 1: Understand the API Endpoint
The Aspose.Tasks Cloud API uses the same endpoint for creating custom properties as for standard ones:
POST /tasks/{name}/documentproperties/{propertyName}
Where:
{name}
is the name of your MS Project file stored in the Aspose Cloud Storage{propertyName}
is the name of the custom document property you want to create
The key difference is that propertyName
will be a name that doesn’t exist in the standard set of document properties.
Step 2: Plan Your Custom Properties
Before implementation, decide what custom properties would benefit your project management workflow. Common examples include:
- ProjectID: A unique identifier for the project in your system
- Department: The department responsible for the project
- Budget: Financial allocation information
- Priority: Custom priority rating
- ApprovalStatus: Current status in approval workflow
- LastReviewDate: Date of last review
- ClientName: Associated client
Step 3: Execute the API Request
Let’s see how to add custom document properties using different approaches:
Using cURL
Try it yourself with this cURL command:
curl -X POST "https://api.aspose.cloud/v3.0/tasks/YourProjectFile.mpp/documentproperties/ProjectID" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-H "authorization: Bearer YOUR_ACCESS_TOKEN" \
-d "{ \"link\": { \"href\": \"\", \"rel\": \"\", \"type\": \"\", \"title\": \"\" }, \"name\": \"ProjectID\", \"value\": \"PRJ-2025-042\"}"
Note: Replace YourProjectFile.mpp
with your actual project filename, ProjectID
with your desired custom property name, and YOUR_ACCESS_TOKEN
with your authentication token.
Using Python
Here’s a complete Python example:
# Tutorial Code Example - Add Non-Existing Document Property in Python
import requests
import json
# Authentication credentials
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"
# Get access token
auth_url = "https://api.aspose.cloud/connect/token"
auth_data = {
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret
}
auth_headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json"
}
auth_response = requests.post(auth_url, data=auth_data, headers=auth_headers)
access_token = auth_response.json().get("access_token")
# API endpoint for creating custom document property
file_name = "Home_move_plan.mpp" # Your project file name
property_name = "ProjectID" # Custom property name
api_endpoint = f"https://api.aspose.cloud/v3.0/tasks/{file_name}/documentproperties/{property_name}"
# Request headers
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"Accept": "application/json"
}
# Request body
property_data = {
"link": {
"href": "",
"rel": "",
"type": "",
"title": ""
},
"name": property_name,
"value": "PRJ-2025-042" # The value for your custom property
}
# Execute request
response = requests.post(api_endpoint, headers=headers, json=property_data)
# Process and display results
if response.status_code == 200 or response.status_code == 201:
property_info = response.json().get("property", {})
print(f"Custom property added successfully:")
print(f"Name: {property_info.get('name')}")
print(f"Value: {property_info.get('value')}")
# Optionally: Add more custom properties
# This demonstrates adding multiple custom properties in sequence
custom_properties = [
{"name": "Department", "value": "Engineering"},
{"name": "Priority", "value": "High"},
{"name": "ClientName", "value": "Acme Corporation"}
]
print("\nAdding additional custom properties:")
for prop in custom_properties:
prop_name = prop["name"]
prop_endpoint = f"https://api.aspose.cloud/v3.0/tasks/{file_name}/documentproperties/{prop_name}"
prop_data = {
"link": {
"href": "",
"rel": "",
"type": "",
"title": ""
},
"name": prop_name,
"value": prop["value"]
}
prop_response = requests.post(prop_endpoint, headers=headers, json=prop_data)
if prop_response.status_code == 200 or prop_response.status_code == 201:
prop_info = prop_response.json().get("property", {})
print(f"- Added {prop_info.get('name')}: {prop_info.get('value')}")
else:
print(f"- Error adding {prop_name}: {prop_response.status_code}")
else:
print(f"Error: {response.status_code}")
print(response.text)
# Verify all custom properties by retrieving all document properties
print("\nVerifying all document properties:")
properties_endpoint = f"https://api.aspose.cloud/v3.0/tasks/{file_name}/documentproperties"
properties_response = requests.get(properties_endpoint, headers={"Authorization": f"Bearer {access_token}", "Accept": "application/json"})
if properties_response.status_code == 200:
properties = properties_response.json().get("properties", {}).get("list", [])
print(f"Found {len(properties)} document properties:")
# Filter to show only our custom properties
custom_props = [p for p in properties if p.get('name') in ['ProjectID', 'Department', 'Priority', 'ClientName']]
for prop in custom_props:
print(f"{prop.get('name')}: {prop.get('value')}")
Using C#
// Tutorial Code Example - Add Non-Existing Document Property in C#
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace AsposeTasksCloudTutorial
{
class Program
{
// Your Aspose Cloud credentials
private const string ClientId = "YOUR_CLIENT_ID";
private const string ClientSecret = "YOUR_CLIENT_SECRET";
static async Task Main(string[] args)
{
// Get the access token
var token = await GetAccessToken();
// Add custom document property
await AddCustomProperty(token, "Home_move_plan.mpp", "ProjectID", "PRJ-2025-042");
// Add more custom properties to demonstrate batch processing
var customProperties = new List<(string Name, string Value)>
{
("Department", "Engineering"),
("Priority", "High"),
("ClientName", "Acme Corporation")
};
Console.WriteLine("\nAdding additional custom properties:");
foreach (var prop in customProperties)
{
await AddCustomProperty(token, "Home_move_plan.mpp", prop.Name, prop.Value);
}
// Verify all properties
Console.WriteLine("\nVerifying all custom properties:");
await VerifyCustomProperties(token, "Home_move_plan.mpp");
}
static async Task<string> GetAccessToken()
{
using (var client = new HttpClient())
{
// Prepare the request to get the access token
var requestContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", ClientId),
new KeyValuePair<string, string>("client_secret", ClientSecret)
});
// Make the request
var response = await client.PostAsync("https://api.aspose.cloud/connect/token", requestContent);
response.EnsureSuccessStatusCode();
// Parse the response
var responseContent = await response.Content.ReadAsStringAsync();
using (JsonDocument document = JsonDocument.Parse(responseContent))
{
return document.RootElement.GetProperty("access_token").GetString();
}
}
}
static async Task AddCustomProperty(string accessToken, string fileName, string propertyName, string propertyValue)
{
using (var client = new HttpClient())
{
// Set the authorization header
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// Prepare the request body
var propertyData = new
{
link = new { href = "", rel = "", type = "", title = "" },
name = propertyName,
value = propertyValue
};
var content = new StringContent(
JsonSerializer.Serialize(propertyData),
Encoding.UTF8,
"application/json");
// Make the request to create custom document property
var response = await client.PostAsync(
$"https://api.aspose.cloud/v3.0/tasks/{fileName}/documentproperties/{propertyName}",
content);
response.EnsureSuccessStatusCode();
// Parse and display the response
var responseContent = await response.Content.ReadAsStringAsync();
// Extract property information from the JSON response
using (JsonDocument document = JsonDocument.Parse(responseContent))
{
var property = document.RootElement.GetProperty("property");
string name = property.GetProperty("name").GetString();
string value = property.GetProperty("value").GetString();
Console.WriteLine($"Added custom property - {name}: {value}");
}
}
}
static async Task VerifyCustomProperties(string accessToken, string fileName)
{
using (var client = new HttpClient())
{
// Set the authorization header
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// Make the request to get all document properties
var response = await client.GetAsync(
$"https://api.aspose.cloud/v3.0/tasks/{fileName}/documentproperties");
response.EnsureSuccessStatusCode();
// Parse and display the custom properties
var responseContent = await response.Content.ReadAsStringAsync();
using (JsonDocument document = JsonDocument.Parse(responseContent))
{
var propertiesList = document.RootElement
.GetProperty("properties")
.GetProperty("list");
var customPropertyNames = new[] { "ProjectID", "Department", "Priority", "ClientName" };
foreach (var element in propertiesList.EnumerateArray())
{
string name = element.GetProperty("name").GetString();
// Only display our custom properties
if (Array.IndexOf(customPropertyNames, name) >= 0)
{
string value = element.GetProperty("value").GetString();
Console.WriteLine($"{name}: {value}");
}
}
}
}
}
}
}
Using Java
// Tutorial Code Example - Add Non-Existing Document Property in Java
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
public class AddNonExistingDocumentPropertyTutorial {
private static final String CLIENT_ID = "YOUR_CLIENT_ID";
private static final String CLIENT_SECRET = "YOUR_CLIENT_SECRET";
private static final HttpClient httpClient = HttpClient.newHttpClient();
public static void main(String[] args) throws IOException, InterruptedException {
// Get access token
String accessToken = getAccessToken();
// Add first custom document property
String fileName = "Home_move_plan.mpp";
String propertyName = "ProjectID";
String propertyValue = "PRJ-2025-042";
addCustomProperty(accessToken, fileName, propertyName, propertyValue);
// Add more custom properties to demonstrate batch processing
System.out.println("\nAdding additional custom properties:");
List<Property> customProperties = new ArrayList<>();
customProperties.add(new Property("Department", "Engineering"));
customProperties.add(new Property("Priority", "High"));
customProperties.add(new Property("ClientName", "Acme Corporation"));
for (Property prop : customProperties) {
addCustomProperty(accessToken, fileName, prop.name, prop.value);
}
// Verify all properties
System.out.println("\nVerifying all custom properties:");
verifyCustomProperties(accessToken, fileName);
}
private static String getAccessToken() throws IOException, InterruptedException {
// Prepare the request to get the access token
String authData = "grant_type=client_credentials&client_id=" + CLIENT_ID + "&client_secret=" + CLIENT_SECRET;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.aspose.cloud/connect/token"))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(authData))
.build();
// Make the request
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// Parse the response
JSONObject jsonResponse = new JSONObject(response.body());
return jsonResponse.getString("access_token");
}
private static void addCustomProperty(String accessToken, String fileName, String propertyName, String propertyValue)
throws IOException, InterruptedException {
// URL encode the file name and property name
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
String encodedPropertyName = URLEncoder.encode(propertyName, StandardCharsets.UTF_8);
// Create the property JSON payload
JSONObject linkObject = new JSONObject();
linkObject.put("href", "");
linkObject.put("rel", "");
linkObject.put("type", "");
linkObject.put("title", "");
JSONObject propertyObject = new JSONObject();
propertyObject.put("link", linkObject);
propertyObject.put("name", propertyName);
propertyObject.put("value", propertyValue);
// Prepare the request to create custom document property
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.aspose.cloud/v3.0/tasks/" + encodedFileName +
"/documentproperties/" + encodedPropertyName))
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(propertyObject.toString()))
.build();
// Make the request
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// Parse and display the response
JSONObject jsonResponse = new JSONObject(response.body());
if (response.statusCode() == 200 || response.statusCode() == 201) {
JSONObject property = jsonResponse.getJSONObject("property");
System.out.println("Added custom property - " + property.getString("name") + ": " + property.getString("value"));
} else {
System.out.println("Error: " + response.statusCode());
System.out.println(jsonResponse.toString(2));
}
}
private static void verifyCustomProperties(String accessToken, String fileName)
throws IOException, InterruptedException {
// URL encode the file name
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
// Prepare the request to get all document properties
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.aspose.cloud/v3.0/tasks/" + encodedFileName + "/documentproperties"))
.header("Authorization", "Bearer " + accessToken)
.GET()
.build();
// Make the request
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// Parse and display the custom properties
JSONObject jsonResponse = new JSONObject(response.body());
if (response.statusCode() == 200) {
JSONArray propertiesList = jsonResponse.getJSONObject("properties").getJSONArray("list");
// List of custom property names we're interested in
List<String> customPropertyNames = Arrays.asList("ProjectID", "Department", "Priority", "ClientName");
for (int i = 0; i < propertiesList.length(); i++) {
JSONObject property = propertiesList.getJSONObject(i);
String name = property.getString("name");
// Only display our custom properties
if (customPropertyNames.contains(name)) {
String value = property.getString("value");
System.out.println(name + ": " + value);
}
}
} else {
System.out.println("Error verifying properties: " + response.statusCode());
System.out.println(jsonResponse.toString(2));
}
}
// Simple class to hold property name-value pairs
static class Property {
String name;
String value;
Property(String name, String value) {
this.name = name;
this.value = value;
}
}
}
Step 4: Understand the Response
When successful, the API returns a JSON response similar to what you’d see when creating standard properties:
{
"code": 0,
"status": "OK",
"property": {
"link": {
"href": "https://api.aspose.cloud/v3.0/tasks/Home_move_plan.mpp/documentproperties/ProjectID",
"rel": "self",
"type": "text/json",
"title": "ProjectID"
},
"name": "ProjectID",
"value": "PRJ-2025-042"
}
}
The key information is in the property
object, which contains:
name
: The name of your custom propertyvalue
: The value you set for the propertylink
: Reference information for the property
Step 5: Best Practices for Custom Properties
When working with custom document properties, follow these best practices:
Naming Conventions
- Use clear, descriptive names that indicate the property’s purpose
- Avoid spaces and special characters when possible
- Follow a consistent naming pattern across all projects
- Consider prefixing organization-specific properties (e.g., “ACME_ProjectID”)
Value Consistency
- Use consistent data formats, especially for dates and numbers
- Consider standardizing values (e.g., “High/Medium/Low” for priorities)
- Document the expected format for each custom property
Property Management
- Create a central registry of custom properties used in your organization
- Document the purpose and expected values for each custom property
- Implement validation for custom property values when possible
- Consider batch processing for setting multiple custom properties at once
Data Types for Custom Properties
Custom properties can store different types of information:
Text Properties
Most custom properties will be text-based. You can store any string value:
{
"name": "Department",
"value": "Engineering"
}
Date Properties
For date information, provide the date in ISO format:
{
"name": "ReviewDate",
"value": "2025-04-23T12:00:00Z"
}
Numeric Properties
For numeric values, provide the value as a string containing only numbers:
{
"name": "Priority",
"value": "1"
}
Boolean Properties
For boolean values, use “true” or “false” as text:
{
"name": "IsApproved",
"value": "true"
}
Try It Yourself
Now it’s your turn to practice! Try adding these custom properties to your project file:
- Add a “ProjectCode” property with a unique identifier
- Create a “ResponsibleTeam” property with a department or team name
- Add a “StartupCost” property with a numeric value
- Create a “LastReviewDate” property with today’s date in ISO format
For an additional challenge, try adding multiple custom properties in a single program run and then verify they were all created correctly.
Troubleshooting Tips
If you encounter issues while following this tutorial, check these common problems:
- 400 Bad Request: Verify your JSON payload is properly formatted
- Property Already Exists: Ensure you’re not trying to create a property that already exists
- Inconsistent Behavior: Some MS Project versions might handle custom properties differently
- Missing Properties After Save: Ensure the file format supports custom properties (MPP is recommended)
What You’ve Learned
In this tutorial, you’ve learned how to:
- Create custom, non-existing document properties in MS Project files
- Structure the JSON payload for custom property creation
- Implement naming conventions and best practices for custom properties
- Use different data types for custom property values
- Verify custom property creation
- Process multiple custom properties in sequence
Further Practice
To reinforce your learning:
- Create a property management tool that adds a standard set of custom properties to all project files
- Implement validation for custom property values before setting them
- Build a reporting tool that extracts and summarizes custom properties from multiple project files
- Create a migration script that adds your organization’s standard custom properties to legacy project files
Completing the Learning Path
Congratulations! You’ve completed the full tutorial series on working with document properties in Aspose.Tasks Cloud API. You now have the knowledge to:
- Retrieve all document properties from project files
- Access specific document properties by name
- Create new standard document properties
- Edit existing document properties
- Add custom, non-existing document properties
These skills provide a solid foundation for implementing robust document metadata management in your project management applications.
Helpful Resources
Have questions about this tutorial series? Feel free to post them on our support forum for assistance!