BizTalk Server: How to retain the original file name after flattening XML file
Scenario
This is a common scenario: XML Files are received from a file location, transformed by a map to a flat file schema , flattened by a flat file assembler pipeline component and sent to another file location.
It is also common for the files to have meaningful names and the flattened file need to have the same name, but with a different extension. So if Orders_0001_20110121.xml comes in, Orders_0001_20110121.txt has to come out, for example.
Solution
Flattening the file
While it is not the scope of this article to explain how to flatten a file, I will mention that a Flat File Schema has to be created, and I do that using the Flat File Schema Creation Wizard. Also, a custom pipeline has to be created and the Flat File Assembler pipeline component has to be added to it. For the purpose of this article I have a created a Send pipeline and dropped the Flat File Assembler in the Assemble stage of the pipeline.
Naming the target file using the source name with a different extension
To achieve that we will modify the content of the ReceivedFileName promoted property using a custom pipeline component, and will retrieve this promoted property on the send port using the %SourceFileName% macro.
Implementation
Start by creating a custom pipeline component that will remove the filename extension from the ReceivedFileName promoted property.
Here is the entire implementation of the Custom Pipeline Component:
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Message.Interop;
using System;
using System.Collections;
using System.ComponentModel;
using System.IO;
namespace ACME.BizTalkProject.CustomPipelineComponents
{
[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
[ComponentCategory(CategoryTypes.CATID_Any)]
[System.Runtime.InteropServices.Guid("ba4d77fd-2008-4295-94b6-bd2b2ea7ae0c")]
public class RemoveExtensionFromReceivedFileNameContextProperty : IBaseComponent, Microsoft.BizTalk.Component.Interop.IComponent, IComponentUI, IPersistPropertyBag
{
#region IBaseComponent
string IBaseComponent.Description
{
get { return "RemoveExtensionFromReceivedFileNameContextProperty pipeline component"; }
}
string IBaseComponent.Name
{
get { return "RemoveExtensionFromReceivedFileNameContextProperty"; }
}
string IBaseComponent.Version
{
get { return "1.0.0.0"; }
}
#endregion
#region IComponentUI
[Browsable(false)]
IntPtr IComponentUI.Icon
{
get { return IntPtr.Zero; }
}
System.Collections.IEnumerator IComponentUI.Validate(object projectSystem)
{
IEnumerator enumerator = null;
return enumerator;
}
#endregion
#region IPersistPropertyBag
void IPersistPropertyBag.GetClassID(out Guid classID)
{
classID = new System.Guid("ba4d77fd-2008-4295-94b6-bd2b2ea7ae0c");
}
void IPersistPropertyBag.InitNew()
{
// Not implemented
}
void IPersistPropertyBag.Load(IPropertyBag pb, int errorLog)
{
// There are no modifiable parameters. Code below is for sample use only.
//string val = (string)ReadPropertyBag(pb, "PaddingCharacter");
//if (val != null) PaddingCharacter = val;
}
private static object ReadPropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName)
{
object val = null;
try
{
pb.Read(propName, out val, 0);
}
catch (ArgumentException)
{
return val;
}
catch (Exception ex)
{
throw new ApplicationException(ex.Message);
}
return val;
}
public void Save(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, Boolean fClearDirty, Boolean fSaveAllProperties)
{
// There are no modifiable parameters. Code below is for sample use only.
//object val = (object)PaddingCharacter;
//WritePropertyBag(pb, "PaddingCharacter", val);
}
private static void WritePropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName, object val)
{
try
{
pb.Write(propName, ref val);
}
catch (Exception ex)
{
throw new ApplicationException(ex.Message);
}
}
#endregion
#region IComponent
public IBaseMessage Execute(IPipelineContext pc, IBaseMessage inmsg)
{
IBaseMessageContext context = inmsg.Context;
if (context != null)
{
string inFileName = context.Read("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties").ToString();
string outFileName = Path.GetFileNameWithoutExtension(inFileName);
context.Write("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties", outFileName);
pc.ResourceTracker.AddResource(inFileName);
pc.ResourceTracker.AddResource(outFileName);
}
return inmsg;
}
#endregion
}
}
Now add this component to the Encode stage of the send pipeline used by your send port. If you are flattening the file and have a custom pipeline for that , you will want to add the component to the same custom pipeline.
If you need details about how to create and use Custom Pipeline Components, you can find them on MSDN at Developing Custom Pipeline Components
To complete this implementation we need to use the %SourceFileName% macro on the send port. On your send port, in the FILE Transport Properties window, make the filename %SourceFileName%.txt
Conclusion
Now every message picked up from a FILE location that is sent to another file location by a pipeline using the custom pipeline component above will retain the original file name with the .TXT filename extension.
See Also
Another important place to find an extensive amount of BizTalk related articles is the TechNet Wiki itself. The best entry point is BizTalk Server Resources on the TechNet Wiki.