Хорошо, я ломал голову над этой проблемой в течение последних нескольких дней без решения (рыскание в Интернете и сообщениях StackOverflow для потенциальных решений безрезультатно), так что, возможно, кто-то еще знает, что здесь происходит.
Я пытаюсь разобрать JMDict с помощью SimpleXML, но получаю очень странные ошибки . Другая странная вещь заключается в том, что ошибки возникают только при отладке на Android (SDK 23, версия 6.0.1), а не локально на моем компьютере (в качестве модульного теста). В настоящее время я получаю 2 ошибки:
Ошибка 1
org.xmlpull.v1.XmlPullParserException: Unexpected token (position:TEXT\n@399:1 in java.io.InputStreamReader@e2fbd29)
Я знаю, что причиной этой ошибки является комментарий между объявлением DTD и корневым элементом, а также новая строка. Проходит при удалении.
<!ENTITY joc "jocular, humorous term">
<!ENTITY anat "anatomical term">
]>
<!-- JMdict created: 2016-04-26 -->
<JMdict>
<entry>
Но почему это происходит? Как я могу подавить эту ошибку? И что еще более важно, почему это происходит только тогда, когда я запускаю его в Android? (Он отлично работает в моем модульном тесте)
Ошибка 2
04-27 23:58:13.038 9527-9527/ca.fuwafuwa.kaku W/System.err: org.xmlpull.v1.XmlPullParserException: unresolved: &n; (position:ENTITY_REF null@408:9 in java.io.InputStreamReader@bc1fe42)
Ошибка, которая вызывает это, вероятно:
<entry>
<ent_seq>1000000</ent_seq>
<r_ele>
<reb>ヽ</reb>
</r_ele>
<r_ele>
<reb>くりかえし</reb>
</r_ele>
<sense>
<pos>&n;</pos> <------------------------------------ THIS GUY
<gloss>repetition mark in katakana</gloss>
<gloss xml:lang="ita">simbolo di ripetizione in katakana</gloss>
</sense>
</entry>
За исключением того, что это определенно определено в DTD:
<!ENTITY n "noun (common) (futsuumeishi)">
Предпринятые попытки
И опять же, обе эти ошибки НЕ возникают при запуске в качестве модульного теста (на компьютере, а не на Android-устройстве/эмуляторе), и весь JMDict анализируется правильно. Вот модульный тест:
public class ExampleUnitTest {
@BeforeClass
public static void ClassSetup(){
// Needed because there's a 64000 default limit in the JDK
// I didn't see this limit on Android for some reason
System.setProperty("jdk.xml.entityExpansionLimit", "0");
}
@Test
public void wtfXmlSrsly() throws Exception {
Serializer serializer = new Persister();
File file = new File("D:\\Android\\JMDictOriginal.xml");
JmDict dict = serializer.read(JmDict.class, file, false);
}
}
Вот версия для Android, которая великолепно провалилась:
public void parseDict() throws Exception{
Log.d(TAG, "INITIALIZING DICTIONARY");
long startTime = System.currentTimeMillis();
Serializer serializer = new Persister();
String fileLoc = mContext.getExternalFilesDir(null).getAbsolutePath();
File file = new File(fileLoc, "JMDict.xml");
Log.d(TAG, file.getAbsolutePath());
JmDict dict = serializer.read(JmDict.class, file, false);
Log.d(TAG, String.format("FINISHED, TOOK %d", System.currentTimeMillis() - startTime));
}
Насколько я могу судить, они в основном делают то же самое.
Объекты десериализации SimpleXML
@Root(name="JMdict")
public class JmDict {
@ElementList(entry = "entry", inline = true)
List<JmEntry> entry;
}
@Root(name="entry")
public class JmEntry {
@Element(name = "ent_seq")
private String ent_seq;
@ElementList(entry = "k_ele", inline = true, required = false)
private List<JmKEle> k_ele;
@ElementList(entry = "r_ele", inline = true)
private List<JmREle> r_ele;
@Element(name = "info", required = false)
private JmInfo info;
@ElementList(entry = "sense", inline = true)
private List<JmSense> sense;
}
@Root(name = "k_ele")
public class JmKEle {
@Element(name = "keb")
private String keb;
@ElementList(entry = "ke_inf", inline = true, required = false)
private List<String> ke_inf;
@ElementList(entry = "ke_pri", inline = true, required = false)
private List<String> ke_pri;
}
@Root(name = "r_ele")
public class JmREle {
@Element(name = "reb")
private String reb;
@Element(name = "re_nokanji", required = false)
private String re_nokanji;
@ElementList(entry = "re_restr", inline = true, required = false)
private List<String> re_restr;
@ElementList(entry = "re_inf", inline = true, required = false)
private List<String> re_inf;
@ElementList(entry = "re_pri", inline = true, required = false)
private List<String> re_pri;
}
@Root(name = "info")
public class JmInfo {
@ElementList(entry = "links", inline = true, required = false)
private List<Links> links;
@ElementList(entry = "bibl", inline = true, required = false)
private List<Bibl> bibl;
@ElementList(entry = "etym", inline = true, required = false)
private List<String> etym;
@ElementList(entry = "audit", inline = true, required = false)
private List<Audit> audit;
}
@Root(name = "links")
public class Links {
@Element(name = "link_tag")
private String link_tag;
@Element(name = "link_desc")
private String link_desc;
@Element(name = "link_uri")
private String link_uri;
}
@Root(name = "bibl")
public class Bibl {
@Element(name = "bib_tag", required = false)
private String bib_tag;
@Element(name = "bib_txt", required = false)
private String bib_txt;
}
@Root(name = "audit")
public class Audit {
@Element(name = "upd_date")
private String upd_date;
@Element(name = "upd_detl")
private String upd_detl;
}
@Root(name = "sense")
public class JmSense {
@ElementList(entry = "stagk", inline = true, required = false)
private List<String> stagk;
@ElementList(entry = "stagr", inline = true, required = false)
private List<String> stagr;
@ElementList(entry = "pos", inline = true, required = false)
private List<String> pos;
@ElementList(entry = "xref", inline = true, required = false)
private List<String> xref;
@ElementList(entry = "ant", inline = true, required = false)
private List<String> ant;
@ElementList(entry = "field", inline = true, required = false)
private List<String> field;
@ElementList(entry = "misc", inline = true, required = false)
private List<String> misc;
@ElementList(entry = "s_inf", inline = true, required = false)
private List<String> s_inf;
@ElementList(entry = "lsource", inline = true, required = false)
private List<String> lsource;
@ElementList(entry = "dial", inline = true, required = false)
private List<String> dial;
@ElementList(entry = "gloss", inline = true, required = false)
private List<String> gloss;
@ElementList(entry = "example", inline = true, required = false)
private List<String> example;
}
Альтернативы
В качестве альтернативы, если кто-то может предложить платформу для синтаксического анализа XML на Android, которая действительно работает для этого варианта использования, это тоже сработает. Он также должен обрабатывать этот случай (SimpleXML не очень хорошо с этим справляется):
<!ELEMENT gloss (#PCDATA | pri)*>
<!ELEMENT pri (#PCDATA)>
<!-- Above breaks down into: -->
<gloss>GLOSS TEXT</gloss>
<!-- or -->
<gloss><pri>PRI TEXT</pri></gloss>
Я думал, что синтаксический анализ XML уже решен. Я никогда не ожидал, что застряну на синтаксическом анализе XML на 3 дня подряд :/
Обновлять
Хорошо, я почти уверен, что сейчас что-то сломалось на Android, и он не может анализировать объекты DTD или что-то в этом роде. Я прочитал здесь, что вы должны исключить некоторые зависимости, которые по умолчанию присутствуют в Android - может там как-то поменялась реализация?
Я даже не смог разобрать этот очень простой файл XML с объявлением ENTITY:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE XmlTest [
<!ELEMENT XmlTest (sometest, atest*)>
<!ELEMENT sometest (#PCDATA) >
<!ELEMENT atest (#PCDATA) >
<!ENTITY noun "noun">
]><XmlTest>
<sometest>sometest</sometest>
<atest>test1</atest>
<atest>&noun;</atest>
</XmlTest>
Это привело к:
org.xmlpull.v1.XmlPullParserException: unresolved: &noun; (position:ENTITY_REF null@10:18 in java.io.InputStreamReader@4c793e1)