Select Page

Dwa środowiska

Głównym założeniem planu minimum jest przygotowanie skryptu, który dałoby się uruchomić z konsoli. Dzięki temu będziemy mogli wczytywać nasze pliki lokalnie, bez potrzeby korzystania z jakichkolwiek serwerów. W tym celu użyjemy node.js. W trakcie prób okazało się jednak, że to rozwiązanie ma jedną, zasadniczą wadę – bardzo ciężko uzyskać informacje na temat poszczególnych elementów naszej struktury. Żeby ułatwić sobie zadanie, wczytamy nasz plik również do przeglądarki. Dzięki Dev Toolsom dostaniemy szczegółowe informacje na temat interesujących nas fragmentów.

Przeglądarka

Wczytanie pliku w przeglądarce najłatwiej przeprowadzić ładując go z serwera. W tym celu do katalogu tests wrzuciłem trzy testowe pliki XML. Dlaczego tak? Żeby każdy mógł z nich skorzytać nie przejmując się CORS (Cross-Origin Resource Sharing).
Samo wczytywanie XML przeprowadzone jest przy użyciu XMLHttpRequest. Nie czuję się w tym temacie komfortowo, więc może w przyszłości spróbuję się z nim zmierzyć i coś napisać.
Plik HTML jest bardzo prosty i służy tylko jako kontener na naszego XMLa.
Jak widać, ładujemy jedynie plik scripts.js, którego zadaniem będzie wczytać zawartość pliku XML i wstawić ją w miejsce znacznika <p>.

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Interpretacja tagów</title>
</head>
<body>
	<h1>Wczytywanie XML na ekran (element » office:document-content, property » childNodes) i do konsoli.</h1>
	<p class="xml-content"></p><
	<script type="text/javascript" src="./scripts/scripts.js"></script>
</body>
</html>

Plik JavaScript to niewiele więcej niż użycie XMLHttpRequest.

//Odczytujemy pliki XML z serwera przez XMLHttpRequest
//Dla testów przygotowane są trzy pliki, przypisane do urlOne, urlTwo, urlThee
const xhr = new XMLHttpRequest(),
	urlOne = 'http://blog.grzegorzlaszczyk.pl/tests/content-one-para.xml',
	urlTwo = 'http://blog.grzegorzlaszczyk.pl/tests/content-para-char.xml',
	urlThree = 'http://blog.grzegorzlaszczyk.pl/tests/content-styled-unstyled.xml';

//zmieniamy zmienne odpowiadające za url poszczególnych plików
xhr.open( 'GET', urlOne, true );
xhr.send( null );
xhr.onload = success;
xhr.onerror = error;

function success( event ) {
	if ( xhr.status == 200 ) {
		screenOutput( xhr.responseText );
		consoleOutput( xhr.responseXML.childNodes[ 0 ]);
	} else {
		console.log( xhr.statusText );
	}
}
function error( event ) {
	console.log( 'We have an error here' );
}
//Wyświetlamy zawartość pliku XML na ekran
function screenOutput( content ) {
	let container = document.querySelector( '.xml-content' );
	container.outerHTML = content;
}
//Wyświetlamy zawartość pliku XML do konsoli
function consoleOutput( content ) {
	console.log( content );
}

Na samym początku zdefiniowane są obiekt xhr i trzy zmienne (urlOne,urlTwo , urlThree), które przechowują adresy poszczególnych plików XML. Następnie otwieramy połączenie XMLHttpRequest, wysyłamy żądanie i w zależności od tego jak zakończy się ta operacja wywołujemy funkcję success (xhr.onload) lub error (xhr.onerror).
Warto zauważyć, że funkcja success na ekranie wyświetla xhr.responseText, w konsoli zaś xhr.responseXML. Dzięki temu zarówno w drzewie DOM naszej strony jak i w konsoli zobaczymy kod XML.
To pozwala skorzytać z Dev Tools i dokładnie zapoznać się ze wszystkimi właściwościami wczytanego pliku co widać na zrzucie ekranu.

XML file in browser

XML file loaded into browser window.

Node.js

'use strict';
const fs = require( 'fs' ),
	jsdom = require( 'jsdom' ),
	util = require( 'util' ),
	urlOne = '../../TestSamples/OnePara/content-one-para.xml',
	urlTwo = '../../TestSamples/ParaCharStylesAndDefault/content-para-char.xml',
	urlThree = '../../TestSamples/StyledAndUnstyledText/content-styled-unstyled.xml';

function nodeInfo( element ) {
	console.log( `nodeName >> ${element.nodeName}` );
	console.log( `nodeValue >> ${element.nodeValue}` );
	console.log( `nodeAttributes >> ${ element.attributes }` );
	console.log( `nodeType >> ${element.nodeType}` );
	console.log( `nodeChildren >> ${element.childNodes}` );
	console.log( `innerHTML >> ${element.innerHTML}` );
}

function children( node ) {
	const childs = Array.from( node.childNodes );
	if ( childs.length > 0 ) {
		childs.map( child => children( child ));
	} else {
		if ( node.nodeType === 3 ) {
			return nodeInfo( node );
		}
	}

}

jsdom.env({
	file: urlOne,
	parsingMode: 'xml',
	done( error, window ) {
		if ( error ) {
			console.log( error );
		}
		const nodeWindow = window,
			myXML = Array.from( nodeWindow.document.getElementsByTagName( 'office:text' ));
		console.log( myXML[ 0 ].innerHTML );
		myXML.map( node => children( node ));
	}
});

Podobnie jak w przeglądarce, tutaj również definiujemy zmienne przechowujące lokalizacje poszczególnych plików XML. Tym razem jednak, wszystko odbywa się lokalnie.
Najważniejsza część tego skryptu to użycie jsdom.env(). Wczytany zostanie plik z lokalizacji, jaką określiliśmy (w tym przypadku urlOne). Jeśli wczytywanie zakończy się sukcesem, do zmiennej nodeWindow przypisujemy obiekt window, zwrócony przez jsdom. Zmienna myXML przechowuje zawartość znacznika <office:text> i zamienia go na tablicę, po której następnie możemy się poruszać rekurencyjne przy pomocy metody map. Ta wywołuje funkcję nodeInfo() dla węzłów, które są elementami (node.nodeType === 3). Dzięki temu możemy sprawdzić, jakie informacje na temat każdego elementu jesteśmy w stanie uzyskać.

XML file loaded into console

XML file content loaded into console

Jak łatwo zauważyć po wywołaniu w terminalu komendy node xml2md.js, informacje o poszczególnych elementach nie są zbyt przejrzyste i intuicyje. Musimy też pamiętać, że za każdym razem mamy do czynienia z tablicą, po której musimy iterować. Nawet jeśli wynikiem jest tylko jeden element (jak zawartość <office:text>) i tak musimy operować wskaźnikami dla tablicy.

Co dalej?

Następnym krokiem będzie próba wyciągnięcia ze znaczników XML samego tekstu i zapisanie go do zmiennej (tablica? string?) z zachowaniem poszczególnych akapitów. Przy tej okazji można odczytać atrybuty każdego znacznika i zapisać je w osobnej zmiennej, albo doczepić (z możliwością ich konwertowania – odpowiednia funkcja) do naszego tekstu.