【问题标题】:Loading XML without given namespace as EMF Model: "Package with uri 'null' not found" - Exception在没有给定命名空间的情况下加载 XML 作为 EMF 模型:“找不到带有 uri 'null' 的包” - 异常
【发布时间】:2017-08-01 23:22:39
【问题描述】:

我有一个 XSD 文件,我将其转换为一个 ecore 模型,并从中生成模型代码。现在我想为该架构加载一个 xml 文件,但不断收到错误:

org.eclipse.emf.ecore.xmi.PackageNotFoundException: 
Package with uri 'null' not found.
(file:/C:/Users/mboeschen/safety/devel/eclipse_plugins...
/de.offis.etas.load/examples/minimal.xml, 2, 7)

由于这是直接在我的 xml 文件中的根标记之后,我怀疑在读取根标记后出现问题。

我的代码如下:

public static void main(String[] args) throws IOException {

    MinimalPackage.eINSTANCE.eClass();  
    MinimalPackage packageInstance = MinimalPackage.eINSTANCE;
    Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
    Map<String, Object> m = reg.getExtensionToFactoryMap();
    m.put("*", new XMLResourceFactoryImpl());

    // Obtain a new resource set
    ResourceSet resSet = new ResourceSetImpl();
    resSet.setResourceFactoryRegistry(reg);

    resSet.getPackageRegistry().put(MinimalPackage.eNS_URI,
            MinimalPackage.eINSTANCE);
    resSet.getPackageRegistry().put(null,
            MinimalPackage.eINSTANCE);

    // Get the resource
    URI uri = URI
    .createFileURI("C:/Users/mboeschen/safety/devel/eclipse_plugins...
                    /de.offis.etas.load/examples/minimal.xml");
    Resource resource = resSet.getResource(uri, true);
    RootType r = (RootType) resource.getContents().get(0);

    System.out.println(r);

架构文件如下所示:

<?xml version="1.0" encoding="US-ASCII"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" >
<xs:element name="Root">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="Inner" type="MyType">
            </xs:element>
        </xs:sequence>
    </xs:complexType>
</xs:element>
<xs:complexType name="MyType">
    <xs:sequence> </xs:sequence>
</xs:complexType>
</xs:schema>

这是xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<Root>
<Inner>
</Inner>
</Root>

有什么想法吗?任何帮助表示赞赏!

【问题讨论】:

  • 您是如何创建 XMI 文件的?它看起来不正确,例如 Root 元素没有任何 xmlns 属性。
  • 我对 XML 无能为力。他们以这种方式提供给我:-(

标签: java eclipse-emf


【解决方案1】:

在过去的两年里,我已经完善了@Severin 为我的项目提出的解决方案。因此,我给出一个额外的答案如下:

当通过XMLResource 加载资源时,会调用几个帮助类来解析 XML 并将其映射到 EMF/Ecore。这些类中的大多数都可以通过更改加载选项来替换。有几个地方可以做到这一点:

  • ResourceSet 具有默认加载选项
  • Resource 默认加载选项(通常通过生成的ResourceFactory 设置)
  • Resource.load() 方法可以提供加载选项

根据您拥有的访问权限(您自己生成的代码与第 3 方生成的代码)以及加载资源的位置/频率,您需要使用其中一种替代方法来添加到加载选项中。

加载选项以键值映射的形式出现。例如,键由ResourceXMLResource 中的常量定义。

对于最初描述的问题有两个关键点:

  • XMLResource.OPTION_MISSING_PACKAGE_HANDLER 提供了一个处理程序,如果 EMF 找不到合适的 EPackage,则调用该处理程序
  • XMLResource.OPTION_EXTENDED_META_DATA 产生一个布尔值(指示是否使用扩展元数据,通常是 true),或一个具体实例 ExtendedMetaData,它提供 XML 和 Ecore 之间的各种映射信息。

对于简单的情况,当

  • 只涉及一个命名空间和一个 EPackage
  • 资源的内容类型是明确的(即您/知道/该资源包含与您的 EPackage 匹配的内容)

您可以像这样提供MissingPackageHandler 实现:

MissingPackageHandler mph = new MissingPackageHandler() {
  @Override
  public EPackage getPackage(final String nsURI) {
    return MyModelEPackage.eINSTANCE;
  }
};

而且至少在我的项目中,我还需要设置这个ExtendedMetaData

BasicExtendedMetaData bemd = new BasicExtendedMetaData(new EPackageRegistryImpl(EPackage.Registry.INSTANCE)) {
  @Override
  protected boolean isFeatureNamespaceMatchingLax() {
    return true;
  }
};

如果涉及多个EPackages 和命名空间,那么MissingPackageHandler 可能过于高级,需要直接通过ExtendedMetaData 实现来区分。

按照@Severin 的回答,可以这样完成:

BasicExtendedMetaData bemd = new BasicExtendedMetaData() {
  private static final String NAMESPACE_GOOGLE_EXT_2_2 = "http://www.google.com/kml/ext/2.2";
  private static final String NAMESPACE_OPENGIS_2_2 = "http://www.opengis.net/kml/2.2";
  private static final String NAMESPACE_DEFAULT = NAMESPACE_OPENGIS_2_2;

  @Override
  public EPackage getPackage(String namespace) {
    if(namespace==null) {
      namespace = NAMESPACE_DEFAULT;
    }
    return super.getPackage(namespace);
  }

  @Override
  public EStructuralFeature getElement(String namespace, String name) {
    // try to find feature in OPENGIS package
    EStructuralFeature result = super.getElement(NAMESPACE_OPENGIS_2_2, name);
    if (feature == null) {
      // if not found, try GOOGLE_EXT
      feature = super.getElement(NAMESPACE_GOOGLE_EXT_2_2, name);
    }
    return feature;
  }
});

【讨论】:

    【解决方案2】:

    当您生成模型时,您可能会生成一个 [YourModelName]ResourceFactoryImpl 类。我的 KML 编辑器遇到了同样的问题,我就是这样解决的:

        public class OgckmlResourceFactoryImpl extends ResourceFactoryImpl {
                ...
            /**
             * Creates an instance of the resource.
             * <!-- begin-user-doc -->
             * <!-- end-user-doc -->
             * @generated NOT
             */
            @Override
            public Resource createResource(URI uri) {
                XMLResource result = new OgckmlResourceImpl(uri);
                setupOptions(result);
                return result;
            }
    
            protected void setupOptions(XMLResource result)
            {
                ...
                result.getDefaultLoadOptions().put(XMLResource.OPTION_EXTENDED_META_DATA, new BasicExtendedMetaData()
                {
                    private static final String NAMESPACE_GOOGLE_EXT_2_2 = "http://www.google.com/kml/ext/2.2";
                    private static final String NAMESPACE_OPENGIS_2_2 = "http://www.opengis.net/kml/2.2";
                    private static final String NAMESPACE_DEFAULT = NAMESPACE_OPENGIS_2_2;
    
                    @Override
                    public EPackage getPackage(String namespace) {
                        if(namespace==null){
                            namespace = NAMESPACE_DEFAULT;
                        }
                        return super.getPackage(namespace);
                    }
    
                    @Override
                    public EStructuralFeature getElement(String namespace, String name)
                    {
                        if (feature == null)
                            feature = super.getElement(NAMESPACE_OPENGIS_2_2, name);
                        if (feature == null)
                            feature = super.getElement(NAMESPACE_GOOGLE_EXT_2_2, name);
                        return feature;
                    }
            });
    ...
        }
        }
    

    【讨论】:

    • 此解决方案有效(几乎...)。我还必须覆盖BasicExtendedMetadata#getElement(EClass, String, String),并且在这里给出的代码中,getElement 有一个遗漏,因为feature 变量没有声明。此外,如果选项应该只使用一次,它们可以从客户端代码传递给resource.load()。那么生成的代码就不用修改了。
    • @StefanWinkler 你能详细说明一下吗?你需要做什么来覆盖BasicExtendedMetadata#getElement(EClass, String, String)?从客户端代码传递给resource.load() 是什么意思?这些中的任何一个是否会修复根目录中没有 xmlns 属性的第 3 方生成的 XML 的编辑器?
    • @tony_tiger 我现在创建了一个更详细的答案,因为我注意到原始答案是 2 岁,并且我已经针对我的案例微调了选项。如果生成的代码和加载代码都是 3rd 方并且您不能修改任何一个,我唯一能想到的就是尝试对生成的 3rd 方资源工厂进行子类化,在那里添加选项,然后尝试在其中注册您的子类全局 EMF Resource.Factory.Registry。但是你需要注意时机,因为你想覆盖现有的工厂,而不是自己被覆盖......
    【解决方案3】:

    从 ATL 的角度来看,我有同样的问题,我做了什么来避免 "PackageNotFoundException" 的问题:拳头你不应该让你的模型命名空间为 NULL,识别 URI 的命名空间 “nsURI ” 将有助于识别代码生成器“xml 文件”中的 Ecore 模型。因此,只需命名 nsURI ,任何名称都可以匹配您的 EPackage。我也重写了 xsi:schemeLocation="...." manulay,也许这没有意义,但最终它解决了问题

    【讨论】:

      猜你喜欢
      • 2020-04-10
      • 2018-06-30
      • 1970-01-01
      • 2015-08-22
      • 1970-01-01
      • 1970-01-01
      • 2011-11-03
      • 1970-01-01
      相关资源
      最近更新 更多