İki XML dosyası nasıl karşılaştırılır ve değişiklik görülür
İki XML dosyasını karşılaştırmanın en hızlı yolu, ikisini de yan yana bir diff aracına yapıştırmak, aynı şekilde biçimlendirmek ve vurguladığı satırları okumaktır. Karşılaştırmanın kendisi kolay kısımdır. İnsanları yanıltan şey gürültüdür: yeniden sıralanmış öznitelikler, etiketler arasındaki boşluklar ve namespace önekleri, aynı anlama gelen iki dosyanın hiç ortak yanı yokmuş gibi görünmesine neden olabilir.
Bu rehber temiz, güvenilir bir XML diff'in nasıl elde edileceğini anlatıyor. Eşdeğer iki belgenin kâğıt üzerinde neden ayrıştığına, bilmeye değer yöntemlere ve takip edebileceğiniz işlenmiş bir örneğe bakacağız. Yalnızca aracı istiyorsanız, XML karşılaştırma sayfamız bunların hepsini tarayıcıda yapar.
XML dosyaları neden aldatıcı şekilde karşılaştırması zordur
XML'in katı bir grameri vardır (W3C XML spesifikasyonuna bakın), ancak yazarlara metni nasıl yerleştireceklerine dair büyük bir serbestlik tanır. İki belge tam olarak aynı verileri tanımlayabilir ve yine de bayt bayt farklı olabilir. Düz metin diff'i bunların hiçbirini anlamaz, bu yüzden hepsini işaretler.
Akılda tutulması gereken kilit gerçek şu: XML'de bir öğenin özniteliklerinin sırası
anlamlı değildir.
XML Information Set
öznitelikleri sırasız bir küme olarak ele alır. Yani
<user id="7" role="admin"/> ve
<user role="admin" id="7"/> aynı bilgiyi taşır; bir satır diff'i
onları kırmızı ve yeşile boyasa bile. Öğe sırası ise genellikle önemlidir.
| Diff'te gördüğünüz | Gerçek bir değişiklik mi? | Ne yapmalı |
|---|---|---|
| Farklı sırada öznitelikler | Hayır, öznitelik sırası anlamlı değildir | Her iki tarafı canonicalize edin |
| 2 boşluk vs 4 boşluk girinti | Hayır | Her iki tarafı aynı şekilde biçimlendirin |
| Öğeler arasındaki boşluk | Genellikle hayır | Biçimlendirin veya önemsiz boşluğu çıkarın |
<br/> vs <br></br> | Hayır, aynı boş öğe | Her iki tarafı canonicalize edin |
| Aynı URI için farklı bir namespace öneki | Hayır, önekler keyfi etiketlerdir | Önek yerine namespace URI'sine göre karşılaştırın |
| Farklı sırada alt öğeler | Genellikle evet, öğe sırası önemlidir | İnceleyin, bu büyük olasılıkla gerçek |
O son satır, dikkat edilmesi gerekendir. Öznitelik sırası serbesttir, ancak alt öğelerin sırası çoğu şemada belgenin bir parçasıdır. Bir parser'ın tüm bunları nasıl gördüğüne dair ayrıntıyı istiyorsanız, MDN'de DOMParser ile XML ayrıştırma konusunda sağlam bir kaynak var.
XML karşılaştırmanın dört yolu ve her birine ne zaman başvurulur
Tek bir en iyi yöntem yoktur. Dosyaların nerede olduğuna ve ne öğrenmeye çalıştığınıza bağlıdır. Yaygın seçeneklerin nasıl kıyaslandığı şöyle.
| Yöntem | En uygun | Çaba | XML'i anlar mı? |
|---|---|---|---|
| Gözle inceleme | Minik dosyalar, bir iki öğe | Düşük | Hayır, parser sizsiniz |
| Çevrimiçi diff aracı | Hızlı kontroller, her yerden yapıştırma | Düşük | Biçimlendirmeyle evet |
Komut satırı (xmllint) | Diskteki dosyalar, betikleme, kanonik biçim | Orta | Evet, --c14n ile |
IDE veya git diff | Zaten bir depoda olan dosyalar | Commit edilmişse düşük | Varsayılan olarak satır tabanlı |
Çoğu kişi için bir tarayıcı aracı hızda kazanır: kurulacak bir şey yok ve bir
yapılandırma dosyasından ya da bir SOAP yanıtından doğrudan bir parça
yapıştırabilirsiniz. İşin püf noktası, sıradaki konumuz olan biçimlendirme
gürültüsüdür. Terminalde yaşıyorsanız,
libxml2'nin
xmllint aracı bilinmesi gereken araçtır.
En hızlı temiz karşılaştırma, adım adım
Birisi bana iki yapılandırma dosyası verip "ne farklı?" diye sorduğunda kullandığım rutin bu. Yaklaşık on beş saniye sürer.
- XML karşılaştırma aracını açın.
- Orijinali sola, yeni sürümü sağa yapıştırın.
- Her iki tarafta Biçimlendir'e tıklayın, böylece aynı girintiyi paylaşsınlar.
- Gerçek farkları arayın. Yeşil eklenen, kırmızı çıkarılandır; değişen bir değer her renkten biri olarak görünür.
- Yalnızca öznitelik yeniden sıralaması veya boşluk olan satırları göz ardı edin.
Üçüncü adım, işin büyük kısmıdır. Her iki belge aynı girintiyi kullandığında, vurgulanacak tek şey gerçekten değişen şeydir. Diff motorumuz Google'ın diff-match-patch üzerine kurulmuştur; uzun dosyalarda bile hızlı kalmak için önce satır satır karşılaştırır.
İşlenmiş bir örnek
Bir servis yapılandırmasındaki bir değişikliği incelediğinizi varsayın. İşte öncesi:
<user id="7" role="editor">
<name>Ada Lovelace</name>
<active>true</active>
<seats>3</seats>
</user>
Ve işte bir takım arkadaşının size verdiği haliyle sonrası:
<user role="admin" id="7">
<name>Ada Lovelace</name>
<active>true</active>
<seats>5</seats>
<team>platform</team>
</user>
Bunları ham bir satır diff'ine atın ve ilk satır değişmiş gibi görünür, çünkü
id ve role yer değiştirdi. İkisini de biçimlendirin,
anlama göre karşılaştırın, ve gerçek hikâye kısa:
| Düğüm | Önce | Sonra | Değişiklik |
|---|---|---|---|
@role | editor | admin | Değiştirildi |
seats | 3 | 5 | Değiştirildi |
team | — | platform | Eklendi |
@id | 7 | 7 | Değişiklik yok (yalnızca taşındı) |
name | Ada Lovelace | Ada Lovelace | Değişiklik yok |
Üç gerçek düzenleme: bir rol yükseltmesi, bir koltuk sayısı ve yeni bir team öğesi.
Öznitelik takası gürültüydü. editor'dan admin'e bu terfi,
incelemede yakalamak istediğiniz türden bir şeydir ve diff'in yanlışlıkla işaretlediği
bir satırın altına gömülünce gözden kaçırması kolaydır.
Kanonik XML: gürültüyü göz ardı etmenin doğru yolu
Her iki tarafı biçimlendirmek girintiyi halleder, ancak tam da bu sorun için tasarlanmış bir standart var. W3C tarafından Canonical XML 1.1'de tanımlanan kanonik XML, bir belgeyi tek bir normalize edilmiş biçime yeniden yazar: öznitelikler sıralanır, boş öğeler genişletilir, etiketlerdeki boşluk normalize edilir ve varsayılan öznitelikler açık hale getirilir. Eşdeğer iki belge aynı kanonik çıktıyı üretir. Bu, JSON anahtarlarını sıralamanın XML karşılığıdır.
xmllint --c14n old.xml > old.c14n.xml
xmllint --c14n new.xml > new.c14n.xml
diff old.c14n.xml new.c14n.xml
Artık diff yalnızca gerçekten değişen içeriği bildirir, çünkü her iki
dosya da aynı şekilde normalize edilmiştir. Katı kanonik biçim yerine yalnızca okunabilir
bir girinti istiyorsanız, xmllint --format file.xml onu biçimli yazdırır;
bu, tarayıcıda Biçimlendir'e tıklamanın terminal karşılığıdır.
Namespace'ler: herkesin kafasını karıştıran kısım
XML namespace'leri, iki belgenin farklı önek etiketleriyle aynı sözcük dağarcığını
kullanmasına izin verir. Bir URI'ye bağlı <ns1:user> ile
aynı URI'ye bağlı <u:user> aynı öğedir; önek yalnızca yerel
bir takma addır. Bir metin diff'i ns1'e karşı u'yu görür ve
olmayan bir değişikliği işaretler. Çözüm, önek yerine namespace URI'sine göre
karşılaştırmaktır ki bu tam olarak canonicalization'ın yaptığı şeydir. Bu konuda bir
tartışmayı sonlandırmanız gerekirse,
Namespaces in XML
spesifikasyonu başvuru kaynağıdır.
Dikkat edilmesi gereken yaygın tuzaklar
| Tuzak | Neden ısırır | Çözüm |
|---|---|---|
| Karakter kodlaması | Bir UTF-8 ve bir UTF-16 dosyası aynı metni tutabilir ama bayt bayt farklı olabilir | Kodlamayı normalize edin; XML bildirimi bunu belirtir |
| Varlık referansları | & ve düz bir & aynı karakter için ikisi de görünebilir | Canonicalize edin, bu varlıkları tutarlı şekilde çözer |
| CDATA vs kaçışlı metin | <![CDATA[a<b]]> ve a<b aynı metin içeriğidir | Ham baytları değil, ayrıştırılmış değeri karşılaştırın |
| Anlamlı boşluk | xml:space="preserve" içinde boşluklar önemlidir ve çıkarılmamalıdır | Körü körüne kırpmayın; xml:space'e saygı gösterin |
| Kendiliğinden kapanan etiketler | <x/> ve <x></x> aynıdır | İkisinin de aynı şekilde işlenmesi için canonicalize edin |
Metin diff vs yapısal diff
Yukarıdakilerin hepsi bir metin diff'idir: hızlı, görsel ve bir değişikliği okuyan bir kişi için mükemmel. Bir yapısal diff daha ileri gider ve değişikliği XML ağacı açısından tanımlar: bu öznitelik değişti, şu alt öğe bu yola eklendi. Bir programın değişikliği uygulaması gerektiğinde ya da öğe sırası gerçekten önemli olmadığında ve göz ardı edilmesini istediğinizde yapısal bir diff istersiniz. Günlük inceleme için, biçimlendirilmiş iki belgenin metin diff'i fazlasıyla yeterlidir.
İlgili araçlar
XML nadiren uğraştığınız tek formattır. API yüklerini karşılaştırıyorsanız, JSON karşılaştırma aynı fikri JSON'a uygular. İşaretli sayfalar HTML karşılaştırma sayfasında daha kolay okunur ve ortam ayarları config karşılaştırma aracında güzel hizalanır.
Sıkça sorulan sorular
- XML dosyalarını çevrimiçi karşılaştırmak onları bir yere yükler mi?
- comparetext.org'da diff tarayıcınızda çalışır. İki XML dosyası kendi makinenizdeki JavaScript tarafından karşılaştırılır, bu yüzden açıkça Kaydet veya Paylaş'a tıklamadığınız sürece sunucuya hiçbir şey gönderilmez. Bu da onu yapılandırma dosyaları, SOAP mesajları ve her tuş vuruşunda yükleme yapan bir siteye yapıştırmak istemeyeceğiniz diğer veriler için güvenli kılar.
- İki XML dosyam neden her satırı farklı gösteriyor?
- Neredeyse her zaman biçimlendirmedir, gerçek değişiklikler değil. Bir dosya minified veya sekmeyle girintilenmiş, diğeri iki boşlukla, ya da öznitelikler farklı bir sırada. Her iki tarafta Biçimlendir'e tıklayın ki aynı girintiyi kullansınlar. Bundan sonra diff genellikle gerçekten değişen birkaç değere iner. Daha katı bir normalizasyon için önce her iki dosyayı xmllint --c14n ile canonicalize edin.
- XML karşılaştırırken öznitelik sırası önemli mi?
- Hayır. XML'de bir öğenin öznitelikleri sırasız bir kümedir, bu yüzden
<a x="1" y="2"/>ve<a y="2" x="1"/>eşdeğerdir. Düz metin diff'i bunu bilmez ve yeniden sıralamayı bir değişiklik olarak işaretler. Kanonik XML öznitelikleri sabit bir sıraya dizer, bu yüzden karşılaştırmadan önce her iki tarafı canonicalize etmek yanlış pozitifi ortadan kaldırır. Öğe sırası ise genellikle anlamlıdır. - Namespace öneklerini göz ardı ederek XML'i nasıl karşılaştırırım?
- Namespace önekleri bir namespace URI'sinin yerel etiketleridir, bu yüzden aynı URI'ye bağlı
ns1:userveu:useraynı öğedir. Doğru karşılaştırmak için önek yerine URI'ye göre normalize edin. En basit yol her iki belgeyi xmllint --c14n ile canonicalize etmektir; bu, namespace bağlamalarını tutarlı şekilde yeniden yazar, ardından sonuçları diff'lersiniz. Ham bir metin diff'i bunu tek başına yapamaz. - Sayfa donmadan büyük XML dosyalarını karşılaştırabilir miyim?
- Evet, bir noktaya kadar. Satır modlu bir diff, her karakter yerine önce tam satırları karşılaştırdığı için binlerce satırlık dosyalarda hızlı kalır. Çok büyük dosyalar (birkaç megabayt) verileri akıtan xmllint veya git diff gibi bir komut satırı aracıyla daha iyi işlenir. Bir tarayıcıda rahatça kaydırabileceğiniz her şey için çevrimiçi bir diff daha hızlı seçenektir.
- XML'in metin diff'i ile yapısal diff'i arasındaki fark nedir?
- Bir metin diff'i dosyaları satır satır karşılaştırır, tıpkı iki kompozisyonu karşılaştırdığı gibi. Bir yapısal diff XML ağacını anlar, bu yüzden yeniden sıralanmış bir özniteliğin bir değişiklik olmadığını bilir ve eklenen bir öğeyi yoluyla bildirebilir. Metin diff'leri daha hızlıdır ve her iki taraf biçimlendirildikten sonra çoğu inceleme için yeterlidir. Yapısal diff'ler, bir programın değişikliği uygulaması gerektiğinde veya öğe sırasını göz ardı etmek istediğinizde önemlidir.
Denemeye hazır mısınız? Dosyalarınızı XML karşılaştırma aracına yapıştırın ve neyin değiştiğini görün.