尝试在 ANDROID 资源中使用 <!ENTITY 时出现错误:“实体已被引用,但未声明。”

Posted

技术标签:

【中文标题】尝试在 ANDROID 资源中使用 <!ENTITY 时出现错误:“实体已被引用,但未声明。”【英文标题】:Trying to use <!ENTITY in ANDROID resources with error: "The entity was referenced, but not declared." 【发布时间】:2018-11-28 17:55:31 【问题描述】:

我正在按照这个解决方案在我的字符串资源文件中使用 enetities:

Is it possible to do string substitution in android resource XML files directly?

我在资源树中使用了一个外部文件:/res/raw/entities.dtd,它的内容:

<!ENTITY ent_devicename "MyDeviceName">

string.xml资源文件中:

<!DOCTYPE resources [
    <!ENTITY % ent_devicename SYSTEM "../raw/entities.dtd">
    %ent_devicename;
]>

<resources>
    <string name="name">The name is &ent_devicename;</string>
</resources>

但我收到此错误:

实体“ent_devicename”已被引用,但未声明。

如您所见,Android Studio 可以识别外部实体文件:

和实体:

有人可以提供完整的正确示例来使事情正常进行吗? 我的意思是一个完全可编译的 Android Studio 项目,实体声明在一个单独的文件中。

更新 好的,如果你多关注这个 w3schools 链接:

https://www.w3schools.com/xml/xml_dtd_entities.asp

你看到了解决方案:

外部entities.dtd 文件包含

<!ENTITY ent_devicename "MyDeviceName">

然后是新的字符串资源资源:

<?xml version="1.0" encoding="utf-8"?>

    <!DOCTYPE resources [
        <!ENTITY ent_devicename SYSTEM "../raw/entities.dtd">
        ]>

<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="Typos">
    <string name="ent_devicenamxxe2">&ent_devicename;</string>

我将 &lt;!ENTITY % ent_devicename 更改为 &lt;!ENTITY ent_devicename(不再有 %) 我删除了%ent_devicename;

现在它可以编译,但生成的 APK 似乎忽略了实体值(使用空字符串)。所以问题没有解决!

告诉我!

【问题讨论】:

@VicJordan 所需的两个 XML 文件(strings.xml 和entities.dtd)已发布在问题中。没有其他相关文件。 对外部参数实体%ent_devicename;和内部实体&amp;ent_devicename;使用相同的名称是自找麻烦。改变一个或另一个(在定义和使用上保持一致),看看你的问题是否不会消失。 @kjhughes 我重命名了外部文件中的实体名称 但问题仍然存在。 @kjhughes 我用一些技巧来解决这个问题,现在它可以编译,但生成的字符串资源具有空实体值。 您的更新会从字面上读取../raw/entities.dtd 的内容并将它们放在您引用&amp;ent_devicename; 的位置——这可能不是您想要的。有关更详细的帮助,请参阅my answer below。谢谢。 【参考方案1】:

TL;DR 此功能已从 Android Studio 中删除。请参阅错误报告 here。

Android Studio 似乎曾一度支持 XML 外部实体。在我看来,Android Studio 目前应该支持外部实体,因为编辑器没有抱怨 XML 的底层处理实际上并没有按预期进行包含,给出了“引用但未声明”错误。

虽然我没有找到关于 Android Studio 放弃外部实体的明确引用,但由于发现了一个漏洞,这将是有意义的。请参阅Android Developers Susceptible to Data Exposure from XXE Attack 和security write-up。

现在也有可能以某种方式限制对外部实体的访问,但我认为如果是这种情况,Android Studio 会更有帮助。更有可能是由于该漏洞而刚刚删除了该功能。

顺便说一句,我已经尝试过其他答案,但我没有运气 - 只是同样的错误。

编辑:

我刚刚遇到了一个 Medium post,它讨论了 JetBrains 解决 XXE 漏洞的问题。

第二次修改

我在错误跟踪器上找到了一个 reference 禁用。

出于安全原因,这可能已被禁用,需要在解决之前进行完整分析,将其踢到 3.2

出于安全原因(防止 XXE 攻击),这确实在 Change-Id: I2f1978bc5458ba2b2b2d6ffbc9df5710c487a4e4 中被禁用。

它的状态是“不会修复预期的行为”。如果 Studio 已更改为发出一条错误消息,指出该工具出于安全原因已被禁用,那就太好了。

【讨论】:

你的答案是目前最好的,所以我投了赞成票。感谢您收集的所有信息。所以......没有更多的外部实体。太可惜了!【参考方案2】:

这个问题的答案/cmets 的历史对您没有帮助,所以让我们简化并一步一步地解决。请使用我在下面显示的更新后的实体名称,并且请为 XML 文件和包含的实体文件使用一个目录。

1。确定内部实体引用是否正常工作。

string.xml

<!DOCTYPE resources [
  <!ENTITY devicename "MyDeviceName">
]>

<resources>
    <string name="name">The name is &devicename;</string>
</resources>

验证这是否按预期工作:应用程序解析此 XML 文件后,它应该等同于此 XML 文件:

<resources>
    <string name="name">The name is MyDeviceName</string>
</resources>

在 cmets 中明确告诉我您是否能够使第 1 步正常工作。

2。如果需要,添加额外的间接级别。

entities.ent

<!ENTITY devicename "MyDeviceName">

string.xml

<!DOCTYPE resources [
    <!ENTITY % ext_entities SYSTEM "entities.ent">
    %ext_entities;
]>

<resources>
    <string name="name">The name is &devicename;</string>
</resources>

对于使用符合标准的 XML 解析器的 XML 应用程序,string.xml 将再次等同于

<resources>
    <string name="name">The name is MyDeviceName</string>
</resources>

您现在可以共享 entities.ent 中定义的实体。

对于这一步,我要求您注意不要为内部和外部实体重复使用实体名称,并且不要因目录位置不同而增加任何复杂性;将entities.ent 和string.xml 放在同一个目录中。 (一旦你建立了预期的功能,这可以放宽。)

再次,在 cmets 中让我知道您是否能够使第 2 步正常工作。

【讨论】:

不,此解决方案不起作用。我不希望实体文件与字符串资源位于同一文件夹中,因为我正在尝试从 Gradle 变体机制中受益,并且我需要将文件保存在单独的文件夹中(并且它运行良好)。第一步正在工作,但对于第二步,Android Studio 说“未解析的实体引用”。 很高兴听到您已经完成了第一步。接下来,出于调试目的,作为一种临时措施,您能否确定步骤#2 在 entities.entstring.xml 位于同一目录中时有效,并且确切的位置在哪里?使用步骤 #2 中显示的文件和实体名称? Android Studio 允许将该文件放在同一个文件夹中,但是当我编译 (Gradle) 时出现错误,因为它是一个特殊文件夹,我无法将 .dtd 文件放入其中。无论如何,如果我将文件放在同一个文件夹中,问题仍然存在:“未解决的实体参考” 我们无法帮助您调试任意且未指定的构建过程。要知道,如果您能够将构建过程争取到提交,以便 entities.entstring.xmlstring.xml 时位于同一目录中b> 被解析,步骤 #2 中给出的文件将一起工作。下一步,您可以尝试对 entities.ent 使用绝对引用而不是相对引用,以放宽文件必须位于同一目录中的要求。有关文件放置的更多灵活性,请参阅 XML 目录。从这个有利的角度来看,我可以为您提供更多帮助。祝你好运。 @dekaru 外部实体不再起作用 看看这个页面【参考方案3】:

恐怕无法导入外部实体(proof1,proof2)。这是唯一的方法。

<?xml version="1.0"?>
<!DOCTYPE resources [
    <!ENTITY value_a "Value A">
    <!ENTITY value_b "Value B">
    ]>

<resources>
    <string name="app_name">&value_a;</string>
    <string name="app_settings ">&value_b;</string>
</resources>

【讨论】:

好的,但我们不是在谈论浏览器...如果 Android Studio 以正确的方式识别外部引用,我希望 gradle 编译器也是如此。你的反应不是我所期望的。 @Seraphim's,他是对的,我认为,他的回答类似于接受。 您好,您在最新版本的 Android Studio 中尝试过吗?【参考方案4】:

或者,如果您仍然希望避免在 Java/Kotlin 上解析占位符,而是在 XML 文件中解析它们,那么您可以使用我创建的这个小库:https://github.com/LikeTheSalad/android-string-reference,它允许您执行以下操作:

<resources>
    <string name="devicename">MyDeviceName</string>
    <string name="template_name">The name is $devicename</string>
</resources>

然后,您制作/构建项目,将生成一个新文件:

<resources>
    <string name="name">The name is MyDeviceName</string>
</resources>

关于上面提供的 repo 的更多信息。

【讨论】:

以上是关于尝试在 ANDROID 资源中使用 <!ENTITY 时出现错误:“实体已被引用,但未声明。”的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin Android 资源 NotFoundException ZXingNetMobile

android module里面的strings.xml怎么区分中英文

Android WebView从资源原始文件夹加载html文件

无法解析区域设置“en_US”的资源包“*”(Flex、Flash Builder)

无法解析语言环境“ en_US”的资源包“ *”(Flex,Flash Builder)

如何在 Android Jetpack Compose 中使用字符串资源?