Archive for December 2010
Get CData from XML using C#
In my recent work, I wrote a custom tool in C# to import xml data into Tridion. With some trails, I found out that error handling in Tridion Object Model (TOM) is bit different than modern APIs. If there is an API, I would always expect to have custom error objects to handle the exceptions in a better and easy way.
But Tridion doesn’t provide any custom error objects in its 2009 version. Since all are COM based components, we shouldn’t expect the benefits of .Net L. Tridion 2011 is completely based on .Net so I believe we will not have any such problems in its future versions.
Generally TOM throws exceptions as xml string. In my case, below is the message.
<?xml version="1.0" ?>
<tcm:Error xmlns:tcm="http://www.tridion.com/ContentManager/5.0" ErrorCode="80040327" Category="19" Source="Kernel" Severity="2">
<tcm:Line ErrorCode="80040327" Cause="false" MessageID="16137">
<![CDATA[
Unable to save Keyword (tcm:0-0-0).
]]>
<tcm:Token>tcm:0-0-0</tcm:Token>
</tcm:Line>
<tcm:Line ErrorCode="80040327" Cause="true" MessageID="15176">
<![CDATA[
XML validation error. Reason: enumeration constraint failed. The element: '{uuid:FAC1CA6A-C5BC-4CEA-AA98-D11E5EE4CD3C}State' has an invalid value according to its data type."; Source: <State>TAMILNADU</State>;
]]>
<tcm:Token>enumeration constraint failed." The element: '{uuid:FAC1CA6A-C5BC-4CEA-AA98-D11E5EE4CD3C}State' has an invalid value according to its data type. "</tcm:Token>
<tcm:Token><State>TAMILNADU</State></tcm:Token>
</tcm:Line>
<tcm:Details>
<tcm:CallStack>
<tcm:Location>XMLValidation.ValidateXML</tcm:Location>
<tcm:Location>XMLValidation.ValidateXML</tcm:Location>
<tcm:Location>XMLValidation.ValidateCustom</tcm:Location>
<tcm:Location>KeywordBL.Create</tcm:Location>
<tcm:Location>XMLState.Save</tcm:Location>
<tcm:Location>Keyword.Save</tcm:Location>
</tcm:CallStack>
</tcm:Details>
</tcm:Error>
Well as you can see this message, I can’t display the entire message to user so I wanted to display only error code and CData which will help them to trace the exception.
Firstly, I tried to come up with a generic method to extract all CData from XML. XLINQ is my obvious choice for these sorts of generic methods. Finally below is the method which will give you all CData in a List<string> object.
public List<string> GetCDataFromXML(string xmlFilePath)
{
//initialize output object
List<string> result = new List<string>();
//initialize XElement object with xml file path
XElement nodes = XElement.Load(xmlFilePath);
// In case of xml string instead of file path
//XElement nodes = XElement.Parse(xmlFilePath);
//navigates only CData elements
foreach (var cData in nodes.DescendantNodes().OfType<XCData>())
{
result.Add(cData.Value);
}
//returns collection of CData<string>
return result;
}
When I tried this code on another machine, unfortunately the machine had only .Net 2.0. So this time, I had to change code for .Net that is without XLINQ.
public List<string> GetCDataFromXML(string xmlFilePath)
{
//initialize output object
List<string> result = new List<string>();
//initialize xml reader object with xml file path
XmlReader reader = new XmlTextReader(xmlFilePath);
// In case of xml string instead of file path
// XmlReader reader = XmlReader.Create(new StringReader(xmlFilePath));
reader.MoveToContent();
//navigates only CData elements
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.CDATA:
result.Add(reader.Value);
break;
}
}
//returns collection of CData<string>
return result;
}
Both returns the same output as below,
Now I’m able to get all CData in an XML so let’s make more generic so that I can use it wherever I’m handling exceptions. Here’s an implementation of simplified custom exception class using above logic.
public class TCMError
{
List<string> _messages = null;
public string Message
{
get
{
//this is as per my requirement
return this._messages[this._messages.Count - 1];
}
}
public TCMError(string exception)
{
this._messages = new List<string>();
XmlReader reader = new XmlTextReader(exception);
reader.MoveToContent();
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.CDATA:
this._messages.Add(reader.Value);
break;
}
}
}
}
Here’s the method calling…
TCMError tcmError = new TCMError(exception);
Console.WriteLine("Message : {0}", tcmError.Message);
Later I’ve modified my custom class according to my requirements but this is only to understand the pattern.

