65.9K
CodeProject 正在变化。 阅读更多。
Home

NIEM 和 IEPD 简介

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (7投票s)

2009年1月1日

CPOL

16分钟阅读

viewsIcon

58591

downloadIcon

520

由司法部和国土安全部开发的国家信息交换模型“连接信息系统”。

引言

本文介绍了我感兴趣的一个主题,即国家信息交换模型(NIEM)和信息交换包文档(IEPD)。NIEM本身之所以有趣,是因为它试图为各种政府机构之间的信息交换定义一个可扩展的标准。IEPD是特定信息域交换的具体定义。NIEM世界中的一切都基于XML和XSD,IEPD中定义的特定模式可能非常复杂。

NIEM,特别是各种IEPD,有趣之处在于它们定义了通常描述数据模型的许多元数据

  • 实体(XSD复杂类型)
  • 实体字段(XSD简单类型)
  • 实体关系(在XSD关联元素中定义)
  • 查找数据(在XSD约束中定义)

然而,IEPD中定义的模式与典型的关系数据模型(RDM)之间往往存在不匹配;事实上,这与面向对象编程与RDM之间存在的不匹配相同。通过使用extension标签,XSD可用于创建层次数据模型(HDM),而这并不一定很容易映射到RDM。

正是这种层次模型与关系模型之间的不匹配(也称为“阻抗不匹配”这一不恰当的说法)使得将现有的RDM转换为IEPD中定义的模式变得困难(坦率地说,这是一种针对该RDM的解决方案)。但是,如果反过来解决这个问题呢?如果给定IEPD中的模式(当然,这是一个HDM),我们是否可以生成RDM?进一步来说,如果我们使用IEPD的HDM作为创建管理RDM中数据的用户界面(UI)的指导呢?

读者此时可能会想,为什么要将HDM转换为RDM?这是一个好问题,如果我们使用面向对象数据库管理系统(ODBMS)3,我们可以生活在层次数据模型的上下文中。然而,现实情况是,大多数个人和公司都生活在RDM的世界里,使用Oracle、SQL Server或流行的开源关系数据库管理系统之一。诚然,说“因为每个人都在使用RDM,所以我们甚至不去考虑ODBMS”是一个微弱的论点。然而,有一个更有说服力的论点。现实情况是,你可能真的没有奢侈去创建一个新的RDM,事实上,你将不得不创建一个现有RDM与IEPD模式之间的映射。如果你很幸运有机会创建一个新的RDM,那么你可能仍然面临与遗留产品仍在使用的遗留RDM进行数据交换的挑战。无论哪种情况,似乎我们将不得不与RDM世界共存一段时间。

因为我对从现有RDM映射到IEPD的问题不太感兴趣,所以我将专门研究我认为更有趣(也更幸运)的路径,即使用IEPD作为指导来生成RDM和UI。然而,我认为,这对于不得不将现有RDM映射到IEPD的人来说仍然很有趣。为什么?因为IEPD定义了实体、实体字段和实体关系,而这些可能在现有RDM中并未完全建模。更重要的是,关系的基数(一对一与一对多)可能导致在将数据从现有RDM映射到IEPD(反之亦然)时遇到一些有趣的困难。例如,如果你的RDM定义一个人有三个电话号码(家庭、工作、手机),但IEPD定义一个人与电话号码有关系,并且该号码有一个可扩展的“类型”字段(家庭、工作、手机、寻呼机、传真、紧急等),那么你最终将不得不处理将更通用的IEPD数据映射到更受限(且可能非规范化)的RDM。

所以,现在你已经了解了我写这篇文章的动机,并在结束引言之前,请允许我直接引用几页网页的内容来解释NIEM和IEPD是什么。

什么是NIEM?

NIEM,即国家信息交换模型,是美国司法部和国土安全部的一项合作项目。它旨在开发、传播和支持企业级信息交换标准和流程,使各司法管辖区能够在紧急情况下有效共享关键信息,并支持全国各机构的日常运营。1

NIEM能够实现信息共享,重点在于组织在当前或预期的业务实践中交换的信息。NIEM交换开发方法论能够使参与组织之间形成共同的语义理解,并以语义一致的方式格式化数据。NIEM将标准化内容(实际数据交换标准)、提供工具并管理流程。

什么是IEPD?

IEPD,即信息交换包文档,是数据交换的规范,并定义了特定的数据交换。例如,有一个IEPD定义了Amber Alert(执法机构发送的关于疑似绑架儿童的公告或消息)的信息内容和结构……NIEM IEPD规范中的产物是对Global Justice XML Data Model(全球司法XML数据模型)之前IEPD指南工作的扩展,特别是信息交换包文档指南2

其他标准

以下是一些您可能感兴趣的其他信息交换模式

正如你所见,使用模式提供公共信息交换定义不仅仅是司法部所做的,在许多行业中都很普遍。这里提到的模式只是冰山一角。

挖掘IEPD

我将使用司法部(DoJ)的事件/逮捕IEPD(下载链接在该页面最底部),重点关注事件,以确定与事件相关的实体和关系。首先,我们将查看此IEPD中的实体。

工具

我一直在使用Liquid XML Studio 2008来检查此模式。如果您购买此工具,可以使用它生成HTML文档(在“工具”->“生成文档…”下),这对于导航IEPD非常有用。我在这里包含文档,但压缩文件是42MB;不过,您可以从我的服务器这里下载文档。还有其他文档工具,如Innovasys Document X,似乎也能生成类似的文档,但我没有试过。当然,还有Altova的XmlSpy工具。Frank Kilkelly有一个看起来非常不错的免费XSD查看器

您还可以使用NIEM Tools网站在线搜索NIEM模式本身;但是,这不包括扩展NIEM模式的模式。

复杂类型和类型关系

如果您下载上面的IEPD,您首先会发现它令人望而生畏。事件/逮捕IEPD的主要N-Dex(国家数据交换)XSD位于路径:N-DEx-IncidentArrest-2.0.0\xsd\ndexia\ndexia\2.0

让我们首先看几个模式中的复杂类型,以理解类型层次结构的一般模式,以便我们可以创建该层次结构的对象的图。使用Liquid XML Studio 2008生成的文档,注意左侧“模式文档”面板中的七个不同文件夹

打开复杂类型文件夹,向下滚动类型,直到找到“PersonType”。请注意,NIEM-core(nc)中定义了几个PersonType,而ndexia命名空间中有一个。

我们对ndexia命名空间中的那个感兴趣。

查看模式,注意复杂内容扩展

<xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="PersonType" >
...snip...
  <xsd:complexContent mixed="false">
    <xsd:extension base="nc:PersonType">

查看NIEM-core的PersonType,我们看到相同的模式

xsd:complexType xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="PersonType" >
...snip...
  <xsd:complexContent mixed="false">
    <xsd:extension base="s:ComplexObjectType">

其中NIEM-core的PersonType是类型ComplexObjectType的扩展。事实上,类似于所有C#对象都派生自Object,NIEM类型都派生自ComplexObjectTypeSimpleObjectType

如果我们查看另一个类型“AircraftType”,使用文档浏览器,请注意层次结构(Liquid XML生成的文档的一部分)

通过观察复杂内容的扩展基类型,我们现在有了一个模式,可以用来生成复杂类型的对象图。我通过迭代IEPD中的所有顶级元素并递归遍历元素类型的扩展来做到这一点。

一些基础知识 - 加载模式

模式从打开文件浏览器的文件名加载

public Iepd(string filename)
{
  XmlTextReader xtr = new XmlTextReader(filename);
  xsd = XmlSchema.Read(xtr, ValidationHandler);
  xsdSet = new XmlSchemaSet();
  xsdSet.Add(xsd);
  xsdSet.Compile();
  typeChildren = new Dictionary<QName, List<XmlSchemaType>>();
  parentList = new Dictionary<QName, QName>();
}

有必要将模式添加到XmlSchemaSet并编译模式集。构造函数还初始化了几个用于生成层次结构的字典。诚然,生成类型对象图的代码有点笨拙,因为我使用了两个字典!

迭代顶级元素

以下代码说明了如何迭代顶级元素。

public void BuildTypeHierarchyFromElements()
{
  foreach (DictionaryEntry de in xsd.Elements)
  {
    XmlSchemaElement xse = de.Value as XmlSchemaElement;
    XmlSchemaType xst = xse.ElementSchemaType;
    FindParent(xst);
  }
}

查找类型的父项

真正的工作在于查找复杂类型的父项,如复杂内容(具有扩展)所指定的。以下代码验证模式是否具有用于构建对象图的上述模式所需的所有部分。

protected void FindParent(XmlSchemaType xst)
{
  if (xst is XmlSchemaComplexType)
  {
    XmlSchemaComplexType xsct = (XmlSchemaComplexType)xst;

    if (xsct.ContentModel is XmlSchemaComplexContent)
    {
      XmlSchemaComplexContent xscc = 
        (XmlSchemaComplexContent)xsct.ContentModel;

      if (xscc.Content is XmlSchemaComplexContentExtension)
      {
        XmlQualifiedName baseType = 
          ((XmlSchemaComplexContentExtension)xscc.Content).BaseTypeName;
        QName parentKey = new QName(baseType);

        if (!typeChildren.ContainsKey(parentKey))
        {
          typeChildren[parentKey] = new List<XmlSchemaType>();
        }

        if (!typeChildren[parentKey].Contains(xst))
        {
          typeChildren[parentKey].Add(xst);
          QName childQName = new QName(xst.QualifiedName);
          parentList[childQName] = parentKey;
          XmlSchemaType xstParent = FindType(xsd, baseType);
          FindParent(xstParent);
        }
      }
    }
  }
}

FindType方法尝试在父模式中查找类型,如果未找到,则递归遍历导入的模式,直到找到具有正确命名空间的模式,表明该模式定义了所需的类型

protected XmlSchemaType FindType(XmlSchema schema, XmlQualifiedName baseType)
{
  XmlSchemaType xstParent = schema.SchemaTypes[baseType] as XmlSchemaType;

  if (xstParent == null)
  {
    // Find the extension in the imported schemas. Non-recursive search.
    foreach (XmlSchemaImport import in schema.Includes)
    {
      if (import.Namespace == baseType.Namespace)
      {
        xstParent = import.Schema.SchemaTypes[baseType] as XmlSchemaType;
        break;
      }
      else
      {
        xstParent = FindType(import.Schema, baseType);

        if (xstParent != null)
        {
          break;
        }
      }
    }
  }

  return xstParent;
}

填充树

填充类型对象图的TreeView需要找到所有没有父项的类型,然后递归地为每个父项构建节点列表。诚然,这并不完全直观,因为创建的类型层次结构是一种中间结构,而不是一个单一的字典,其中键具有相同字典项的集合作为子类型。

public void PopulateTree()
{
  Dictionary<QName, List<XmlSchemaType>> h = iepd.TypeHierarchy;

  foreach (KeyValuePair<QName, List<XmlSchemaType>> kvp in h)
  {
    // Find all nodes that are root parents.
    if (!iepd.ParentList.ContainsKey(kvp.Key))
    {
      TreeNode tn;
      tn = new TreeNode(kvp.Key.name);
      tn.Tag = kvp.Key;
      tnTypes.Nodes.Add(tn);
      PopulateChildren(iepd, tn, kvp.Key);
    }
  }

  tnTypes.ExpandAll(); 
}

protected void PopulateChildren(Iepd iepd, TreeNode tn, QName qname)
{
  if (iepd.TypeHierarchy.ContainsKey(qname))
  {
    foreach (XmlSchemaType xst in iepd.TypeHierarchy[qname])
    {
      TreeNode tnChild = new TreeNode(xst.QualifiedName.Name);
      tnChild.Tag = new QName(xst.QualifiedName);
      tn.Nodes.Add(tnChild);
      PopulateChildren(iepd, tnChild, new QName(xst.QualifiedName.Name, 
                                      xst.QualifiedName.Namespace));
    }
  }
}

结果

结果是一个以树形结构渲染的对象图。请注意,图如何以易于理解的层次结构反映复杂内容扩展

属性和子属性钻取

让我们再次查看复杂类型PersonType。请注意,ndexia命名空间中为PersonType定义的模式包含两个“增强”(顺便说一句,这是NIEM和NIEM派生模式中另一个非常常见的模式)

<xsd:complexContent mixed="false">
  <xsd:extension base="nc:PersonType">
    <xsd:sequence>
      <xsd:element minOccurs="0" ref="j:PersonAugmentation"/>
      <xsd:element minOccurs="0" ref="ndexia:PersonAugmentation"/>
    </xsd:sequence>
  </xsd:extension>
</xsd:complexContent>

其中一个元素定义在“justice”命名空间中,另一个定义在“ndexia”命名空间中。我们希望能够列出特定类型的所有元素,并进一步钻取这些元素以查看它们的子类型,如下图所示的PersonType类型的截图

因此,IEPD浏览器应用程序的左侧是XSD类型层次结构的树视图,选择图中的一个项会在该类型的元素层次结构中创建树视图。如果我们从OOD(面向对象设计)的角度来看,左侧的树代表类型之间的“is A”关系,右侧的树代表每个类型包含的“has A”项。很明显,仅凭这些信息,我们就可以生成类及其属性来建模模式!

以下代码显示了元素是如何被递归处理以构建元素图的

protected void tnTypes_AfterSelect(object sender, TreeViewEventArgs e)
{
  QName qname = (QName)e.Node.Tag;
  tbNamespace.Text = qname.ns;

  tvElements.Nodes.Clear();
  List<ElementInfo> elInfoList = iepd.GetElements(qname);
  TreeNode tnRoot = new TreeNode("Elements");

  foreach (ElementInfo elInfo in elInfoList)
  {
    TreeNode tn = CreateElementRow(elInfo);
    tnRoot.Nodes.Add(tn);
  }

  tvElements.Nodes.Add(tnRoot);
  tnRoot.Expand();
}

protected TreeNode CreateElementRow(ElementInfo elInfo)
{
  TreeNode tn = new TreeNode();
  tn.Text = elInfo.Name + " (MinOccurs=" + elInfo.MinOccurs + 
            ", MaxOccurs=" + elInfo.MaxOccurs + 
            ", Type=" + elInfo.Type + ")";

  if (elInfo.ChildElements.Count > 0)
  {
    foreach (ElementInfo ei in elInfo.ChildElements)
    {
      TreeNode tnChild = CreateElementRow(ei);
      tn.Nodes.Add(tnChild);
    }
  }

  return tn;
}

特别值得关注的是GetElements方法,坦率地说,它在某种程度上是NIEM模式特定的。您将从代码中看到,有一些特定的限定符模式决定了元素的列表,如下文所述

XmlSchemaType is XmlSchemaComplexType
    -> ContentModel is XmlSchemaComplexContent 
        -> Content is XmlSchemaComplexContentExtension
             -> Particle is XmlSchemaSequence
                  -> Items are XmlSchemaElement
    -> ContentModel is XmlSchemaSimpleContent
         -> Content is XmlSchemaSimpleContentExtension

限定符期望元素是复杂内容扩展中序列的一部分。虽然元素必须是序列的一部分,但它们也可以作为复杂类型的一部分出现,而不仅仅是复杂内容扩展。然而,由于NIEM模型的所有模式类型都派生自ComplexObjectType,因此元素必须是复杂内容扩展序列的一部分;因此,我认为上述模式是正确的,至少对于基于NIEM的模式是如此。

public List<ElementInfo> GetElements(QName qname, 
       out XmlQualifiedName simpleContentType)
{
  simpleContentType = null;
  List<ElementInfo> elInfoList = new List<ElementInfo>();
  XmlSchemaType xst = GetSchemaType(qname);

  if (xst == null)
  {
    XmlSchemaElement xse = GetSchemaElement(qname);
    xst = GetSchemaType(new QName(xse.SchemaTypeName));
  }

  if (xst is XmlSchemaComplexType)
  {
    XmlSchemaComplexType xsct = (XmlSchemaComplexType)xst;

    if (xsct.ContentModel is XmlSchemaComplexContent)
    {
      XmlSchemaComplexContent xscc = 
        (XmlSchemaComplexContent)xsct.ContentModel;

      if (xscc.Content is XmlSchemaComplexContentExtension)
      {
        XmlSchemaComplexContentExtension xscce = 
              (XmlSchemaComplexContentExtension)xscc.Content;

        if (xscce.Particle is XmlSchemaSequence)
        {
          XmlSchemaSequence xss = (XmlSchemaSequence)xscce.Particle;

          foreach (XmlSchemaObject xso in xss.Items)
          {
            if (xso is XmlSchemaElement)
            {
              XmlSchemaElement xse = (XmlSchemaElement)xso;
              ElementInfo elInfo = new ElementInfo(xse, xse.MinOccursString, 
                 xse.MaxOccursString, xse.RefName.Name, xse.SchemaType, xse.RefName);

              if (xse.ElementSchemaType is XmlSchemaComplexType)
              {
                elInfo.DrillIntoType(this);
              }

              elInfoList.Add(elInfo);
            }
          }
        }
      }
    }
    else if (xsct.ContentModel is XmlSchemaSimpleContent)
    {
      XmlSchemaSimpleContent xssc = 
         (XmlSchemaSimpleContent)xsct.ContentModel;

      if (xssc.Content is XmlSchemaSimpleContentExtension)
      {
        XmlSchemaSimpleContentExtension xssce = 
           (XmlSchemaSimpleContentExtension)xssc.Content;
        simpleContentType = xssce.BaseTypeName;
      }
    }
  }

  return elInfoList;
}

查找值

事件/逮捕模式还包括用于查找值的枚举约束,其中包含一个作为长描述的文档元素,以及一个通常是“代码”的枚举值。例如,“NY”是“New York”州的“代码”,这将在该枚举约束的文档中找到。

有两种基本模式可以查找元素的查找值——一种是钻取元素的SimpleContentType,另一种是查找元素的substitutionGroup

钻取SimpleContentType

查看ndexia:PersonTypendexia:PersonAugmentation,我们会找到各种元素,包括PersonFacialHairCode

<xsd:complexType name="PersonAugmentationType">
  <xsd:complexContent>
    <xsd:extension base="ndexia:AugmentationType">
      <xsd:sequence>
        [...snip...]
        <xsd:element ref="ndexia:PersonFacialHairCode" 
          minOccurs="0" maxOccurs="unbounded"/>
        [...snip...]
      </xsd:sequence>
    </xsd:extension>
  </xsd:complexContent>
</xsd:complexType>

该元素定义为

<xsd:element name="PersonFacialHairCode" 
         type="ndexiacodes:PersonFacialHairCodeType" nillable="true">
  <xsd:annotation>
    <xsd:documentation>A code that identifies the type 
         of facial hair the person has.</xsd:documentation>
  </xsd:annotation>
</xsd:element>

PersonFacialHairCodeType类型实际上是一个简单类型,它是面部毛发代码的枚举

<xsd:complexType name="PersonFacialHairCodeType">
  [...snip...]
  <xsd:simpleContent>
    <xsd:extension base="ndexiacodes:PersonFacialHairCodeSimpleType">
      <xsd:attributeGroup ref="s:SimpleObjectAttributeGroup"/>
    </xsd:extension>
  </xsd:simpleContent>
</xsd:complexType>

ElementInfo类已经包含了SimpleContentTypeXmlQualifiedName,因此获取枚举约束的文档和值的“简单”工作

public List<EnumFacet> GetLookupData(ElementInfo elInfo)
{
  List<EnumFacet> enums = new List<EnumFacet>();

  if (elInfo.SimpleContentType != null)
  {
    XmlSchemaType xse = GetSchemaType(elInfo.SimpleContentType);

    if (xse is XmlSchemaSimpleType)
    {
      XmlSchemaSimpleType xsst = xse as XmlSchemaSimpleType;

      if (xsst.Content is XmlSchemaSimpleTypeRestriction)
      {
        XmlSchemaSimpleTypeRestriction xsstr = 
                 xsst.Content as XmlSchemaSimpleTypeRestriction;

        if (xsstr.Facets != null)
        {
          foreach (XmlSchemaObject xso in xsstr.Facets)
          {
            if (xso is XmlSchemaEnumerationFacet)
            {
              XmlSchemaEnumerationFacet xsef = 
                      xso as XmlSchemaEnumerationFacet;
              string doc = GetDocumentation(xsef.Annotation);
              string val = xsef.Value;
              enums.Add(new EnumFacet(val, doc));
            }
          }
        }
      }
    }
  }

  return enums;
}

protected string GetDocumentation(XmlSchemaAnnotation xsa)
{
  StringBuilder sb = new StringBuilder();

  foreach (XmlSchemaObject xso in xsa.Items)
  {
    if (xso is XmlSchemaDocumentation)
    {
      XmlSchemaDocumentation xsd = xso as XmlSchemaDocumentation;

      foreach (XmlNode xn in xsd.Markup)
      {
        sb.Append(xn.InnerText);
        sb.Append("\r\n");
      }
    }
  }

  return sb.ToString();
}

结果是代码和文档的列表,可以用作元素的选取列表。在这种情况下,代码和文档是相同的

SubstitutionGroups

另一种常见模式是使用模式substitutionGroup。我们将查看NIEM-core的PersonEthnicity,它是nc:PersonType的一部分

<xsd:element name="PersonEthnicity" abstract="true">
[...snip...]
</xsd:element>

<xsd:element substitutionGroup="nc:PersonEthnicity" 
   name="PersonEthnicityCode" type="fbi:EthnicityCodeType" nillable="true">
[...snip...]
</xsd:element>

在这里,我们看到PersonEthnicityCodePersonEthnicity的替换组,类型来自fbi命名空间,EthnicityCodeType,然后遵循上面描述的simpleContentType模式

<xsd:complexType name="EthnicityCodeType">
[...snip...]
  <xsd:simpleContent>
    <xsd:extension base="fbi:EthnicityCodeSimpleType">
      <xsd:attributeGroup ref="s:SimpleObjectAttributeGroup"/>
    </xsd:extension>
  </xsd:simpleContent>
</xsd:complexType>

因此,要获取一个人种族信息的查找数据,我们必须检查一个元素,该元素是该类型的替换组。我重写了上述方法,因为这两种模式都有一个通用的代码块来从简单类型中获取枚举约束。

public List<EnumFacet> GetLookupData(ElementInfo elInfo)
{
  List<EnumFacet> enums = new List<EnumFacet>();

  if (elInfo != null)
  {
    if (elInfo.SimpleContentType != null)
    {
      XmlSchemaType xse = GetSchemaType(elInfo.SimpleContentType);

      if (xse is XmlSchemaSimpleType)
      {
        XmlSchemaSimpleType xsst = xse as XmlSchemaSimpleType;
        GetLookupData(enums, xsst);
      }
    }
    else
    {
      // Find an element with this type as the substitutionGroup
      XmlQualifiedName elxqn = elInfo.RefName;
      XmlSchemaElement xse = FindSubstitutionGroupElement(xsd, elxqn);

      if (xse.ElementSchemaType is XmlSchemaComplexType)
      {
        XmlSchemaComplexType xsct = 
           xse.ElementSchemaType as XmlSchemaComplexType;

        if (xsct.ContentModel is XmlSchemaSimpleContent)
        {
          XmlSchemaSimpleContent xssc = 
            xsct.ContentModel as XmlSchemaSimpleContent;

          if (xssc.Content is XmlSchemaSimpleContentExtension)
          {
            XmlSchemaSimpleContentExtension xssce = 
               (XmlSchemaSimpleContentExtension)xssc.Content;
            XmlQualifiedName xqn = xssce.BaseTypeName;
            XmlSchemaType xse2 = GetSchemaType(xqn);

            if (xse2 is XmlSchemaSimpleType)
            {
              XmlSchemaSimpleType xsst = xse2 as XmlSchemaSimpleType;
              GetLookupData(enums, xsst);
            }
          }
        }
      }
    }
  }

  return enums;
}

现在,当使用模式替换组来指定具有简单内容扩展的新元素类型时,我们可以获取枚举约束。

是的,FBI种族枚举非常简单——只有三种类型。

一个更复杂的例子

让我们看看要钻取ndexia:LocationType复杂类型的元素需要什么,以便最终到达LocationState元素,该元素是usps:USStateCodeType类型的替换组,而后者是州列表!

首先,让我们找出usps:USStateCodeType在哪里被使用。通过反向挖掘模式,我们找到了以下层次结构

  1. ndexia:LocationTypenc:LocationType的扩展
  2. nc:LocationType有一个元素nc:LocationAddress
  3. nc:LocationAddress的类型是nc:AddressType
  4. nc:AddressType有一个元素nc:AddressRepresentation
  5. nc:AddressRepresentationnc:StructuredAddressType类型的substitutionGroup的值
  6. nc:StructuredAddressType有一个元素nc:LocationState
  7. nc:LocationStateusps:USStateCodeType类型的substitutionGroup的值

请注意,存在元素层次结构(可以将其视为返回派生自某个其他类的类的属性),以及两个替换组。使用IEPD浏览器,我们已经可以访问AddressRepresentation

现在需要的是发现其替换组是AddressRepresentation的元素,以便我们可以钻取StructuredAddressType的元素

<xsd:complexType name="StructuredAddressType">
  [...snip...]
  <xsd:complexContent>
    <xsd:extension base="s:ComplexObjectType">
      <xsd:sequence>
        <xsd:element ref="nc:AddressDeliveryPoint" minOccurs="0" maxOccurs="2"/>
        <xsd:element ref="nc:LocationCounty" minOccurs="0" maxOccurs="1"/>
        <xsd:element ref="nc:LocationState" minOccurs="0" maxOccurs="1"/>
        <xsd:element ref="nc:LocationCountry" minOccurs="0" maxOccurs="1"/>
        <xsd:element ref="nc:LocationPostalExtensionCode" minOccurs="0" maxOccurs="1"/>
      </xsd:sequence>
    </xsd:extension>
  </xsd:complexContent>
</xsd:complexType>

从上面的模式可以看出,复杂内容扩展中有一些元素应该显示在“Elements”树视图中。第一步是重构代码并构建一个替换组字典。我们这样做是为了只遍历整个模式一次,然后我们可以查找元素以查看它们是否参与了替换组。

protected void BuildSubstitutionGroupDictionary(XmlSchema xsd)
{
  foreach (XmlSchemaObject xso in xsd.Items)
  {
    if (xso is XmlSchemaElement)
    {
      XmlSchemaElement xse = xso as XmlSchemaElement;

      if (xse.SubstitutionGroup != null)
      {
        // The substitution group is the key, the element that redefines
        // the qualified name is the value.
        substGroupMap[xse.SubstitutionGroup] = xse;
      }
    }
  }

  // Look into the schema import/includes as well:
  foreach (XmlSchemaImport import in xsd.Includes)
  {
    BuildSubstitutionGroupDictionary(import.Schema);
  }
}

对上面所示的GetElements方法进行微小修改,使我们能够定位替换组并继续构建元素层次结构。

[...code snippet...]
if (xse.ElementSchemaType is XmlSchemaComplexType)
{
  elInfo.DrillIntoType(this);
  XmlSchemaElement xseSubstGroup;
  substGroupMap.TryGetValue(xse.QualifiedName, out xseSubstGroup);

  if (xseSubstGroup != null)
  {
    elInfo.DrillIntoSubstGroup(this, xseSubstGroup);
  }
}

elInfoList.Add(elInfo);
[...code snippet...]

结果是元素列表现在钻取了替换组,我们可以获取州列表。

结论

最终分析,我们(作为个人、公司、地方机构、州和政府)正越来越多地走向一个信息必须在地方、州和国家边界之间交换的世界,我们需要一种标准化的机制来处理这些数据。NIEM以及司法部等各机构发布的IEPD,以及其他机构发布的其他公共模式,是在朝着以定义的方式交换信息迈进的重要一步。

我将继续这个系列的文章,接下来将探讨IEPD如何指导生成镜像对象图的类,这些类如何映射到关系数据模型,以及IEPD如何指导UI架构(我将特别关注模式的maxOccurs属性以指导数据表示)。

历史 

1/2/2009

  • 修正了niem-core生产版本中由于循环导入而导致的堆栈溢出问题。
  • 修正了Facet文档不存在时的异常。
  • 修正了打开新模式时类型树未被清除的错误。

1/3/2009

  • 替换组映射现在允许多个替换组(参见niem-core.xsd,EntityRepresentation类型)。
  • 修正了元素的循环钻取,例如在niem-core、DNAType、ImageLocation、LocationType 中,最终会得到具有多个替换组的EntityRepresentation。

2009年2月18日发布2.0版本

  • 修正了niem-core生产版本中由于循环导入而导致的堆栈溢出问题。
  • 修正了Facet文档不存在时的异常。
  • 修正了打开新模式时类型树未被清除的错误。
  • 替换组映射现在允许多个替换组(参见niem-core.xsd,EntityRepresentation类型)。
  • 修正了元素的循环钻取,例如在niem-core、DNAType、ImageLocation、LocationType 中,最终会得到具有多个替换组的EntityRepresentation。
  • 现在停止循环引用使用元素类型限定名称,而不是元素限定名称,因为元素限定名称通常是“专业化”的,而它引用的类型是通用的。
  • 现在弹出“已钻取”的元素类型限定名称,因为这会阻止同级和更高级别的类型钻取到已遇到的类型(我们只希望停止循环引用)。
  • 修改了基数以使用(n..m)格式。
  • 修正:例如:DocumentType、DocumentFiledDate,应显示Type=DateType,而不是DocumentFiledDate(我们需要引用类型)。
  • 修正:DateRepresentation是抽象的,并具有简单类型的替换组。这些现在显示在元素树中。
  • 类型层次树现在已排序。
  • 更改了层次树,使其迭代所有全局复杂类型,而不是从全局元素开始并迭代其类型。这为我们提供了全局类型的完整列表。
  • 抽象类型现在显示“anyType”作为其类型。出于某种原因,IsAbstract标志未被设置(参见DateRepresentation)。
  • 对于简单类型替换组,现在在设置简单类型替换组后将simpleContentType置为null。这修正了在树中显示错误类型名称的问题。
  • 元素浏览器现在在节点展开时钻取子元素。这极大地提高了UI的性能,因为其中一些类型具有非常广泛的元素树。
  • 修正了LocationType/.../LocationState:有9个替换组。现在显示了替换组简单类型的正确代码列表。

参考文献 

NIEM和IEPD简介 - CodeProject - 代码之家
© . All rights reserved.