На сегодняшний день роль XML (eXtensible Markup Language — расширяемый язык разметки) в программировании огромна: этот формат используется повсеместно во многих языках программирования. В частности, при разработке приложений на Android XML отвечает за то, что пользователь увидит на экране своего смартфона (интерфейс приложения, строковые ресурсы, векторные изображения и т.д.). Кроме того, данный формат зачастую используется для хранения и передачи различных данных.
Именно из-за того, что XML можно использовать как хранилище данных, встаёт вопрос: как с этими данными взаимодействовать, как получать их из XML и отправлять обратно.
Ранее мы говорили о том, что в Android 8.0 Oreo для хранения конфигураций Wi-Fi сетей теперь используется другой файл (подробнее об этом можно почитать здесь). Поскольку этот файл конфигурации как раз нужного нам формата XML, попробуем распарсить его и вытащить их него нужные данные.
Сама структура XML-файла конфигурации выглядит следующим образом:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<WifiConfigStoreData>
<int name="Version" value="1" />
<NetworkList>
<Network>
<WifiConfiguration>
<string name="ConfigKey">"Kafe Mongol"WPA_PSK</string>
<string name="SSID">"Kafe Mongol"</string>
<null name="BSSID" />
<string name="PreSharedKey">"mongol1234"</string>
<null name="WEPKeys" />
<int name="WEPTxKeyIndex" value="0" />
<boolean name="HiddenSSID" value="false" />
<boolean name="RequirePMF" value="false" />
<byte-array name="AllowedKeyMgmt" num="1">02</byte-array>
<byte-array name="AllowedProtocols" num="1">03</byte-array>
<byte-array name="AllowedAuthAlgos" num="1">01</byte-array>
<byte-array name="AllowedGroupCiphers" num="1">0f</byte-array>
<byte-array name="AllowedPairwiseCiphers" num="1">06</byte-array>
<boolean name="Shared" value="true" />
<int name="Status" value="2" />
<null name="FQDN" />
<null name="ProviderFriendlyName" />
<null name="LinkedNetworksList" />
<string name="DefaultGwMacAddress">6c:19:8f:f5:e3:6e</string>
<boolean name="ValidatedInternetAccess" value="true" />
<boolean name="NoInternetAccessExpected" value="false" />
<int name="UserApproved" value="0" />
<boolean name="MeteredHint" value="false" />
<boolean name="UseExternalScores" value="false" />
<int name="NumAssociation" value="4" />
<int name="CreatorUid" value="1000" />
<string name="CreatorName">android.uid.system:1000</string>
<string name="CreationTime">time=08-30 12:51:06.957</string>
<int name="LastUpdateUid" value="1000" />
<string name="LastUpdateName">android.uid.system:1000</string>
<int name="LastConnectUid" value="1000" />
<boolean name="IsLegacyPasspointConfig" value="false" />
<long-array name="RoamingConsortiumOIs" num="0" />
</WifiConfiguration>
<NetworkStatus>
<string name="SelectionStatus">NETWORK_SELECTION_ENABLED</string>
<string name="DisableReason">NETWORK_SELECTION_ENABLE</string>
<null name="ConnectChoice" />
<long name="ConnectChoiceTimeStamp" value="-1" />
<boolean name="HasEverConnected" value="true" />
</NetworkStatus>
<IpConfiguration>
<string name="IpAssignment">DHCP</string>
<string name="ProxySettings">NONE</string>
</IpConfiguration>
</Network>
...
</NetworkList>
<PasspointConfigData>
<long name="ProviderIndex" value="0" />
</PasspointConfigData>
</WifiConfigStoreData>
Нужные нам данные хранятся внутри тега <WifiConfiguration>, каждый тег обозначает тип значения, затем внутри в виде атрибутов устанавливаются его название и собственно значение.
Для извлечения данных в Android есть полезный интерфейс XmlPullParser. С его помощью можно в цикле while пройтись построчно по каждому тегу в документе и проверить его содержимое. Каждый проход по циклу представляет собой определённое событие, метод getEventType() возвращает тип этого события, например: START_DOCUMENT, END_TAG и др. Переход к следующему тегу осуществляется с помощью метода next().
Извлечём некоторые данные из XML файла. Получившийся код выглядит следующим образом:
private void parseWithXmlPullParser() {
LayoutInflater inflater = getLayoutInflater();
CardView cardView = null;
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(50, 50, 50, 50);
try {
XmlPullParser xmlPullParser = getResources().getXml(R.xml.wifi_config_store);
while (xmlPullParser.getEventType() != XmlPullParser.END_DOCUMENT) {
switch (xmlPullParser.getEventType()) {
case XmlPullParser.START_DOCUMENT: {
if (BuildConfig.DEBUG) {
Log.d("LOG_TAG", "START_DOCUMENT");
}
break;
}
// начало тега
case XmlPullParser.START_TAG: {
if (BuildConfig.DEBUG) {
Log.d("LOG_TAG", "START_TAG: имя тега = "
+ xmlPullParser.getName()
+ ", уровень = "
+ xmlPullParser.getDepth()
+ ", число атрибутов = "
+ xmlPullParser.getAttributeCount());
}
if (xmlPullParser.getName().equals("WifiConfiguration")) {
cardView = (CardView) inflater.inflate(R.layout.wifi_info, null);
cardView.setLayoutParams(params);
}
if (xmlPullParser.getName().equals("string")) {
if (xmlPullParser.getAttributeValue(null, "name").equals("SSID")) {
TextView textViewSSID = cardView.findViewById(R.id.textSsid);
textViewSSID.setText(textViewSSID.getText() + " " + xmlPullParser.nextText());
break;
}
if (xmlPullParser.getAttributeValue(null, "name").equals("PreSharedKey")) {
TextView textViewPreShareKey = cardView.findViewById(R.id.textPreShareKey);
textViewPreShareKey.setText(
textViewPreShareKey.getText() + " " + xmlPullParser.nextText());
break;
}
}
if (xmlPullParser.getName().equals("boolean")) {
if (xmlPullParser.getAttributeValue(null, "name").equals("HiddenSSID")) {
TextView textViewHiddenSSID = cardView.findViewById(R.id.textHiddenSsid);
textViewHiddenSSID.setText(
textViewHiddenSSID.getText() + " " + xmlPullParser.getAttributeValue(null,
"value"));
break;
}
}
break;
}
// конец тега
case XmlPullParser.END_TAG:
if (BuildConfig.DEBUG) {
Log.d("LOG_TAG", "END_TAG: имя тега = " + xmlPullParser.getName());
}
if (xmlPullParser.getName().equals("WifiConfiguration")) {
linearLayout.addView(cardView);
}
break;
// содержимое тега
case XmlPullParser.TEXT:
if (BuildConfig.DEBUG) {
Log.d("LOG_TAG", "текст = " + xmlPullParser.getText());
}
break;
default:
break;
}
xmlPullParser.next();
}
} catch (Throwable e) {
e.printStackTrace();
}
}
В данном случае нас интересует событие START_TAG. Когда это событие наступает, нужно найти нужные данные по типу и имени, и затем забрать из этого тега значение. В результате выполнения цикла выводим всё на экран.
Как видно, реализация довольно проста, разработчику достаточно только понимать, как устроен XML-документ.
Однако это не единственный способ извлечь данные из XML. В Android есть также класс DocumentBuilder, который позволяет получать экземпляры DOM документов из XML DOM (Document Object Model) — это программный интерфейс, благодаря которому можно получать доступ к данным HTML или XML.
Ключевым методом здесь будет являться getElementsByTagName(), который возвращает список тегов с нужным именем, затем внутри этого списка ищется нужный по атрибуту name. Реализация выглядит следующим образом.
private void parseWithDOM() {
LayoutInflater inflater = getLayoutInflater();
CardView cardView = null;
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(50, 50, 50, 50);
try {
InputStream is = getAssets().open("wifi_config_store.xml");
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.parse(is);
Element element = doc.getDocumentElement();
element.normalize();
NodeList nList = doc.getElementsByTagName("WifiConfiguration");
for (int i = 0; i < nList.getLength(); i++) {
cardView = (CardView) inflater.inflate(R.layout.wifi_info, null);
cardView.setLayoutParams(params);
TextView textViewSSID = cardView.findViewById(R.id.textSsid);
TextView textViewPreSharedKey = cardView.findViewById(R.id.textPreShareKey);
TextView textViewHiddenSSID = cardView.findViewById(R.id.textHiddenSsid);
Node node = nList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element elementNode = (Element) node;
NodeList childrenList = elementNode.getElementsByTagName("string");
for (int j = 0; j < childrenList.getLength(); j++) {
Element childNode = (Element) childrenList.item(j);
if (childNode.getAttribute("name").equals("SSID")) {
textViewSSID.setText(textViewSSID.getText() + " " + childNode.getChildNodes().item(0).getNodeValue());
}
if (childNode.getAttribute("name").equals("PreSharedKey")) {
textViewPreSharedKey.setText(textViewPreSharedKey.getText() + " " + childNode.getChildNodes().item(0).getNodeValue());
}
}
NodeList hiddenList = elementNode.getElementsByTagName("boolean");
for (int k = 0; k < hiddenList.getLength(); k++) {
Element childNode = (Element) hiddenList.item(k);
if (childNode.getAttribute("name").equals("HiddenSSID")) {
textViewHiddenSSID.setText(textViewHiddenSSID.getText() + " " + childNode.getAttribute("value"));
}
}
}
linearLayout.addView(cardView);
}
} catch (IOException | ParserConfigurationException | SAXException e) {
e.printStackTrace();
}
}
В результате мы получили аналогичный список, как и при первом случае.
Здесь были приведены не все способы распарсить XML, существует множество различных пользовательских библиотек, предоставляющих такой же функционал. Всё зависит от того, какой способ будет удобнее разработчику в конкретной ситуации.




Спасибо за хорошую статью. Жаль, раньше на нее не попал и парсил документ с помощью строковых методов java)))
Спасибо за хороший пример XML файла