title: How to tell if a string is xml?
body: <p>We have a string field which can contain XML or plain text. The XML contains no <code>&lt;?xml</code> header, and no root element, i.e. is not well formed.</p>
  
  <p>We need to be able to redact XML data, emptying element and attribute values, leaving just their names, so I need to test if this string is XML before it's redacted.</p>
  
  <p>Currently I'm using this approach:</p>
  
  <pre><code>string redact(string eventDetail)
  {
      string detail = eventDetail.Trim();
      if (!detail.StartsWith("&lt;") &amp;&amp; !detail.EndsWith("&gt;")) return eventDetail;
      ...
  </code></pre>
  
  <p>Is there a better way? </p>
  
  <p>Are there any edge cases this approach could miss?</p>
  
  <p>I appreciate I could use <code>XmlDocument.LoadXml</code> and catch <code>XmlException</code>, but this feels like an expensive option, since I already know that a lot of the data will not be in XML.</p>
  
  <p>Here's an example of the XML data, apart from missing a root element (which is omitted to save space, since there will be a lot of data), we can assume it is well formed:</p>
  
  <pre><code>&lt;TableName FirstField="Foo" SecondField="Bar" /&gt; 
  &lt;TableName FirstField="Foo" SecondField="Bar" /&gt; 
  ...
  </code></pre>
  
  <p>Currently we are only using attribute based values, but we may use elements in the future if the data becomes more complex.</p>
  
  <p><strong>SOLUTION</strong> </p>
  
  <p>Based on multiple comments (thanks guys!)</p>
  
  <pre><code>string redact(string eventDetail)
  {
      if (string.IsNullOrEmpty(eventDetail)) return eventDetail; //+1 for unit tests :)
      string detail = eventDetail.Trim();
      if (!detail.StartsWith("&lt;") &amp;&amp; !detail.EndsWith("&gt;")) return eventDetail;
      XmlDocument xml = new XmlDocument();
      try
      {
          xml.LoadXml(string.Format("&lt;Root&gt;{0}&lt;/Root&gt;", detail));
      }
      catch (XmlException e)
      {
          log.WarnFormat("Data NOT redacted. Caught {0} loading eventDetail {1}", e.Message, eventDetail);
          return eventDetail;
      }
      ... // redact
  </code></pre>