Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding missing XML Namespace support to XmlApplier #145

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,31 @@ public class XmlApplier : IApplier
/// <summary>For each column, map from the XML path to the column name</summary>
private SqlMap<string, string> columnPaths;

/// <summary>Map namespace prefixes to namespace URIs</summary>
/// <remarks>If you have a default namespace (without prefix) in your XML document,
/// provide a prefix in the map for that namespace URI and use that prefix in the
/// XPath expression to select the nodes that are in the default namespace.</remarks>
private SqlMap<string, string> namespaceDecls;

/// <summary>New instances are constructed at least once per vertex</summary>
/// <param name="xmlColumnName">In the input row, the name of the column containing XML. The column must be a string.</param>
/// <param name="rowPath">Path of the XML element that contains rows.</param>
/// <param name="columnPaths">For each column, map from the XML path to the column name.
/// It is specified relative to the row element.</param>
/// <remarks>Arguments to appliers must not be column references.
/// The arguments must be able to be calculated at compile time.</remarks>
public XmlApplier(string xmlColumnName, string rowPath, SqlMap<string, string> columnPaths)
/// <param name="namespaceDecls">For each namespace URI in the document that you want to query, map the prefix to the namespace URI.
/// If you have a default namespace (without prefix) in your XML document,
/// provide a prefix in the map for that namespace URI and use that prefix in the
/// XPath expression to select the nodes that are in the default namespace.
/// If there is no namespace URI in the document, the map can be left null.</param>
/// <remarks>Do not rely on static fields because their values will not cross vertices.</remarks>
public XmlApplier(string xmlColumnName, string rowPath, SqlMap<string, string> columnPaths, SqlMap<string, string> namespaceDecls = null)
{
this.xmlColumnName = xmlColumnName;
this.rowPath = rowPath;
this.columnPaths = columnPaths;
this.namespaceDecls = namespaceDecls;
}

/// <summary>Apply is called at least once per instance</summary>
Expand All @@ -81,17 +94,28 @@ public override IEnumerable<IRow> Apply(IRow input, IUpdatableRow output)
{
throw new ArgumentException(string.Format("Column '{0}' must be of type 'string', not '{1}'", column.Name, column.Type.Name));
}
// TODO: Add XML Namespace support and allow document fragments (should also be supported on XmlDomExtractor!).

XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(input.Get<string>(this.xmlColumnName));
foreach (XmlNode xmlNode in xmlDocument.DocumentElement.SelectNodes(this.rowPath))
XmlNamespaceManager nsmanager = new XmlNamespaceManager(xmlDocument.NameTable);

// If namespace declarations have been provided, add them to the namespace manager
if (this.namespaceDecls != null)
{
foreach (var namespaceDecl in this.namespaceDecls)
{
nsmanager.AddNamespace(namespaceDecl.Key, namespaceDecl.Value);
}
}

foreach (XmlNode xmlNode in xmlDocument.DocumentElement.SelectNodes(this.rowPath, nsmanager))
{
// IUpdatableRow implements a builder pattern to save memory allocations,
// so call output.Set in a loop
foreach(IColumn col in output.Schema)
{
var explicitColumnMapping = this.columnPaths.FirstOrDefault(columnPath => columnPath.Value == col.Name);
XmlNode xml = xmlNode.SelectSingleNode(explicitColumnMapping.Key ?? col.Name);
XmlNode xml = xmlNode.SelectSingleNode(explicitColumnMapping.Key ?? col.Name, nsmanager);
output.Set(explicitColumnMapping.Value ?? col.Name, xml == null ? null : xml.InnerXml);
}

Expand Down