Converting Sparx Enterprise Architect Baseline Information from XML into C# objects

In this post I will show you how to easily work with Sparx Enterprise Architect Baseline information in C#.

Baseline processing in Enterprise Architect is exposed via the ProjectClass (EA.Repository.GetProjectInterface()). From there we have to use a two-step approach to get a baseline and its stored information:

  1. Invoke project.GetBaselines() with a PackageGuid to get the stored baselines for a sopecific package.
  2. Invoke project.DoBaselineCompare() with the previous PackageGuid and a BaselineGuid returned in the previous step.

Project.GetBaselines

Suppose we retrieve the baselines of a package (with Guid {50168942-9238-44a6-89F3-B624125BFBAE}) with the baselines as outlined in the figure below. After our call to project.GetBaselines("{50168942-9238-44a6-89F3-B624125BFBAE}", "") we get an XML similar to this:

<?xml version="1.0" encoding="UTF-8"?>
<EA.BaseLines package="{50168942-9238-44a6-89F3-B624125BFBAE}">
   <Baseline name="Requirements" version="1.0" notes="Baseline time: 09/08/2019 19:44:12" guid="{93F584F1-8B22-4eca-AB96-5FA1011975D4}" />
   <Baseline name="Requirements" version="2.0" notes="Baseline time: 09/08/2019 19:44:18" guid="{BD589BFA-5C09-4f2b-A029-F19D7F791341}" />
</EA.BaseLines>
PackageBaselines

PackageBaselines

As we can see every baselines has its own unique guid and a version attribute.

Project.DoBaselineCompare

If we now compare the model against the first baseline (with Guid {93F584F1-8B22-4eca-AB96-5FA1011975D4}) and call project.DoBaselineCompare("{50168942-9238-44a6-89F3-B624125BFBAE}", "{93F584F1-8B22-4eca-AB96-5FA1011975D4}", "") EA will return the follwing XML:

<?xml version="1.0" encoding="UTF-8"?>
<EA.CompareLog>
  <ComparePackage name="Requirements" guid="EAID_50168942_9238_44a6_89F3_B624125BFBAE" packageID="211" comparedBy="edgar" comparedOn="2019-08-09 19:50:13">
  <CompareResults hasChanges="true">
    <CompareItem name="Requirements" type="Package" guid="{50168942-9238-44a6-89F3-B624125BFBAE}" status="Identical">
    <Properties>
      <Property name="Abstract" model="false" baseline="false" status="Identical" />
      <Property name="Alias" status="Identical" />
      <Property name="Author" model="edgar" baseline="edgar" status="Identical" />
      <Property name="Complexity" model="1" baseline="1" status="Identical" />
      <Property name="IsLeaf" model="false" baseline="false" status="Identical" />
      <Property name="IsSpec" model="false" baseline="false" status="Identical" />
      <Property name="IsRoot" model="false" baseline="false" status="Identical" />
      <Property name="Keywords" status="Identical" />
      <Property name="Multiplicity" status="Identical" />
      <Property name="Name" model="Requirements" baseline="Requirements" status="Identical" />
      <Property name="Notes" status="Identical" />
      <Property name="Persistence" status="Identical" />
      <Property name="Phase" model="1.0" baseline="1.0" status="Identical" />
      <Property name="Scope" model="Public" baseline="Public" status="Identical" />
      <Property name="Status" model="Proposed" baseline="Proposed" status="Identical" />
      <Property name="Stereotype" status="Identical" />
      <Property name="Type" model="Package" baseline="Package" status="Identical" />
      <Property name="Version" model="1.0" baseline="1.0" status="Identical" />
      <Property name="Classifier" status="Identical" />
      <Property name="Visibility" status="Identical" />
      <Property name="Concurrency" status="Identical" />
      <Property name="Cardinality" status="Identical" />
      <Property name="Style" status="Identical" />
      <Property name="Advanced Properties" status="Identical" />
    </Properties>
    <CompareItem name="Requirement2" type="Requirement" guid="{EC192A01-B94F-4540-87D9-353C72AEC19F}" status="Changed">
      <Properties>
      <Property name="Abstract" model="false" baseline="false" status="Identical" />
      <Property name="Alias" status="Identical" />
      <Property name="Author" model="edgar" baseline="edgar" status="Identical" />
      <Property name="DateCreated" model="27/07/2019 22:03:41" baseline="27/07/2019 22:03:41" status="Identical" />
      <Property name="DateModified" model="09/08/2019 19:50:03" baseline="27/07/2019 22:03:41" status="Changed" />
      <Property name="Complexity" model="1" baseline="1" status="Identical" />
      <Property name="GenFile" status="Identical" />
      <Property name="GenType" model="<none>" baseline="<none>" status="Identical" />
      <Property name="IsLeaf" model="false" baseline="false" status="Identical" />
      <Property name="IsSpec" model="false" baseline="false" status="Identical" />
      <Property name="IsRoot" model="false" baseline="false" status="Identical" />
      <Property name="Keywords" status="Identical" />
      <Property name="Multiplicity" status="Identical" />
      <Property name="Name" model="Requirement2" baseline="Requirement1" status="Changed" />
      <Property name="Notes" status="Identical" />
      <Property name="ParentPackage" model="Requirements" baseline="Requirements" status="Identical" />
      <Property name="Persistence" status="Identical" />
      <Property name="Phase" model="1.0" baseline="1.0" status="Identical" />
      <Property name="Scope" model="Public" baseline="Public" status="Identical" />
      <Property name="Status" model="Proposed" baseline="Proposed" status="Identical" />
      <Property name="Stereotype" status="Identical" />
      <Property name="Type" model="Requirement" baseline="Requirement" status="Identical" />
      <Property name="Version" model="1.0" baseline="1.0" status="Identical" />
      <Property name="Classifier" status="Identical" />
      <Property name="Visibility" status="Identical" />
      <Property name="Concurrency" status="Identical" />
      <Property name="Cardinality" status="Identical" />
      <Property name="Style" status="Identical" />
      <Property name="Advanced Properties" status="Identical" />
      </Properties>
    </CompareItem>
    </CompareItem>
  </CompareResults>
  </ComparePackage>
</EA.CompareLog>

Side note: The guid attribute in CompareItem is not always a real GUID, but can also contain trailing textual information (for examples with connectors). So we have to check the type first before trying to convert it to a real guid.

The simplified element structure looks like this

EA.CompareLog
  ComparePackage 
    CompareResults
      CompareItem [0..*]
        Properties [0..1]
          Property [0..*]
        CompareItem [0..*]

Each CompareItem and Property has a Status with one of the following contents:

  • Identical, if the model does not differ from the baseline
  • Changed, if there is at least one change in the model from the baseline
  • Baseline only, if the element was deleted from the model
  • Model only, if the element has been created after the baseline has been created

If the baseline and the model are completely the same, then the hasChanges attribute on the CompareResults elements is False – otherwise True.

BaselineComparison Visitor

Looping through the comparison data can be tedious, so I use a simple visitor that calls back my code for the elements I am interested in:

public interface IBaselineComparisonVisitor
{
  void Visit(BaselineComparison baselineComparison);
  void Visit(BaselineComparison baselineComparison, string status);
  void VisitPackage(BaselineComparison.ComparePackageElement element);
  void VisitResults(BaselineComparison.CompareResultsElement element);
  void VisitElements(List<BaselineComparison.CompareItemElement> elements);
  void VisitElements(List<BaselineComparison.CompareItemElement> elements, BaselineComparison.CompareItemElement parent);
  void VisitElement(BaselineComparison.CompareItemElement element, BaselineComparison.CompareItemElement parent);
  void VisitElement(BaselineComparison.CompareItemElement element);
  void VisitProperties(List<BaselineComparison.PropertyElement> elements, BaselineComparison.CompareItemElement parent);
  void VisitProperties(List<BaselineComparison.PropertyElement> elements);
  void VisitProperty(BaselineComparison.PropertyElement element, BaselineComparison.CompareItemElement parent);
  void VisitProperty(BaselineComparison.PropertyElement element);
}

Suppose I only want to retrieve changed element and property count, I create a derived visitor class and only override the following methods:

public class MyBaselineVisitor : BaselineComparisonVisitor
{
  public int ElementCount;
  public int PropertyCount;

  public override void VisitProperty(BaselineComparison.PropertyElement element)
  {
    PropertyCount++;
  }

  public override void VisitElement(BaselineComparison.CompareItemElement element)
  {
    ElementCount++;
  }
}

The code to invoke the comparison in a unit test would then look like this:

[TestMethod]
public void Test()
{
  var baseline = XmlSerialiserBase.Deserialise<BaselineComparison>(BaselineComparisonTest.XML);

  var sut = new MyBaselineVisitor();
  sut.Visit(baseline, BaselineComparisonStatus.Changed);

  Assert.AreEqual(1, sut.ElementCount);
  Assert.AreEqual(2, sut.PropertyCount);
}

The Assert statements verify the correct behaviour (based on the example XML above).

This is actually it. With a small utility classes we no longer need to use MSXML2 and iterating over xml elements but persue a clean approach working with native C# objects.

Complete Example

In the Gist below you find the following files:

  1. DTO for the baseline summary information that you can use with the XmlSerialiser to convert the returned XML into a C# object.
  2. DTO for the baseline comparison result that you can use with the XmlSerialiser to convert the baseline comparison XML into a C# object.
  3. A simple visitor class that helps you iterate over the BaselineComparison object.

Deserialising the XML into the C# classes can be performed like this:

public static T Deserialise<T>(string xml)
  where T : XmlSerialiserBase
{
  using (var stream = new StringReader(xml))
  {
    return (T) new XmlSerializer(typeof(T)).Deserialize(stream);
  }
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: