مشكلة مع فرز XSL
-
30-09-2019 - |
سؤال
أواجه مشكلة في محاولة الفرز مع ملف XSL باستخدام XSLCompileDTransform في CLR4.0. إليكم ملف عينة XML (ملاحظة: هناك مساحة بعد الثانية <foo>
عنصر):
<?xml version="1.0" encoding="utf-8"?>
<reflection>
<apis>
<api id="A">
<foos>
<foo/>
</foos>
</api>
<api id="B">
<foos>
<foo/>
</foos>
</api>
</apis>
</reflection>
عندما أقوم بتطبيق ملف XSL التالي:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.1">
<xsl:template match="/">
<html>
<body>
<table>
<xsl:apply-templates select="/reflection/apis/api">
<xsl:sort select="@id" />
</xsl:apply-templates>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="api">
<tr>
<td>
<xsl:value-of select="@id" />
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
أحصل على النتيجة التالية:
<html>
<body>
<table>
<tr>
<td>B</td>
</tr>
<tr>
<td>A</td>
</tr>
</table>
</body>
</html>
ومع ذلك ، إذا قمت بإزالة المساحة بعد الثانية <foo>
العنصر ، يتم فرز الملف الناتج بشكل صحيح. يبدو أن هذا ربما يكون خطأ في XSLcompileDTransform ، لكنني كنت آمل أن يكون لدى شخص ما حلًا.
تحرير: إذا كان أي شخص يواجه مشكلة في إعادة إنتاجه ، فإليك الرمز الذي أستخدمه:
XslCompiledTransform xslt = new XslCompiledTransform();
XsltSettings transformSettings = new XsltSettings(true, true);
xslt.Load("CreateVSToc.xsl", transformSettings, new XmlUrlResolver());
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreWhitespace = true;
Stream readStream = File.Open("reflection.xml", FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
using (XmlReader reader = XmlReader.Create(readStream, readerSettings))
{
Stream outputStream = File.Open("toc.xml", FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete);
using (XmlWriter writer = XmlWriter.Create(outputStream, xslt.OutputSettings))
{
XsltArgumentList arguments = new XsltArgumentList();
xslt.Transform(reader, arguments, writer);
}
}
المحلول 4
Russ Ferri ، شكرًا على إجابتك. وجهت لي في الاتجاه الصحيح. يبدو أن الخلل في XSLcompileDTransform هو أنه عندما تريد الفرز بسمات عنصر ما ، فإنه في الواقع يفرز قيمة العنصر الطفل الأول لهذا العنصر. لذلك كما أشار روس ، سيؤدي ذلك إلى الفرز بشكل صحيح مع ملف التحويل الأصلي الخاص بي:
<reflection>
<apis>
<api id="C">
<id>C</id>
<foos>
<foo/>
</foos>
</api>
<api id="B">
<id>B</id>
<foos>
<foo/>
</foos>
</api>
</apis>
</reflection>
ولكن هذا سوف:
<reflection>
<apis>
<api id="C">
<anyElementName>C</anyElementName>
<foos>
<foo/>
</foos>
</api>
<api id="B">
<anyElementName>B</anyElementName>
<foos>
<foo/>
</foos>
</api>
</apis>
</reflection>
في الواقع ، يتم تجاهل اسم السمة تمامًا ، لذلك إذا قمت بتشغيل التحويل على شيء مثل هذا:
<reflection>
<apis>
<api id="C">
<anyElementName>Z</anyElementName>
<foos>
<foo/>
</foos>
</api>
<api id="A">
<anyElementName>Y</anyElementName>
<foos>
<foo/>
</foos>
</api>
<api id="B">
<anyElementName>X</anyElementName>
<foos>
<foo/>
</foos>
</api>
</apis>
</reflection>
النتيجة تبدو هكذا:
<html>
<body>
<table>
<tr>
<td>B</td>
</tr>
<tr>
<td>A</td>
</tr>
<tr>
<td>C</td>
</tr>
</table>
</body>
</html>
وهو الفرز الصحيح إذا كنت تقوم بالفرز بواسطة <anyElementName>
عناصر
نصائح أخرى
بشكل مثير للريبة ، إذا كان XML لكل منهما api
يتم تعديل العنصر إلى ما يلي ، سيتم فرز النتيجة كما هو متوقع:
<api id="apiId">
<id>apiId</id>
<foos>
<foo />
</foo>
</api>
علاوة على ذلك ، إذا أ) XML لكل منهما api
يتم تعديل العنصر لإزالة id
تنسب بالكامل
<api>
<id>apiId</id>
<foos>
<foo />
</foo>
</api>
و ب) فقط ثانيا إشارة إلى @id
في ملف XSL يتم تغييره إلى id
, ، ستظل النتيجة مرتبة أبجديًا!
من الممكن أن XslCompiledTransform
تحاول الفرز على عنصر الطفل المسمى id
بدلا من سمة اسمها id
, أو هذا يمكن أن يكون مجرد حظ غبي. في كلتا الحالتين ، قمت بالتحقق من أنه على استعداد للفرز بشكل صحيح على عنصر الطفل المسمى id
.
مع وضع ذلك في الاعتبار ، يمكنني التفكير في حلتين ، لكن كلاهما يتطلب أن يكون لديك مستوى من التحكم في عملية التحويل.
النهج 1: أنت قادر على تغيير XML
قم بتغيير العملية كتابة XML الأصلي لتحديد id
كما العنصر الأول الموجود بواسطة api
عنصر. ثم قم بتحديث XSL لاستبدال المراجع إلى @id
مع id
.
النهج 2: أنت قادر على تشغيل XML مسبقًا قبل تطبيق XSL الخاص بك
استخدم تحويل XSL لتحريك قيمة id
تنسب إلى عنصر طفل api
, ، ثم ضع نفس XSL كما تفعل النهج 1 إلى وثيقة XML الوسيطة. من الواضح أن تحويل المستند مرتين سيكون أقل استحسانًا عند معالجة ملفات XML الكبيرة.
سوف تحصل عليك XSL التالية من XML الأصلي إلى XML الوسيط:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.1">
<!-- recursively copy each element (including root) -->
<xsl:template match="*|/">
<xsl:copy>
<!-- xsl:copy ignores attributes, copy those as well -->
<xsl:copy-of select="@*"/>
<!-- continue to deep copy the element -->
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<!-- for api elements, move the id attribute into an element -->
<xsl:template match="api">
<api>
<id>
<xsl:value-of select="@id"/>
</id>
<!-- continue deep copy of api element contents -->
<xsl:apply-templates />
</api>
</xsl:template>
</xsl:stylesheet>
آمل أن يساعد هذا!
إنه يعمل إذا قام بتغيير نسخة Sytyle إلى "1.0" من أي شيء> = 2.0
لم أحاول إعادة إنتاج المشكلة ، ولا أرى أي شيء خطأ بوضوح في XSL. إليك ما سأستكشفه لمعرفة ما إذا كانت مشكلتك أو خطأ في محرك XSL: حاول إزالة المساحة بعد <foo>
وتغيير معرف "A" إلى "C". هل تحصل على الإخراج بالترتيب "B" ، "C"؟ وبعبارة أخرى ، تأكد من أنه في الواقع الفرز حسب المعرف.