كيف يمكنني استخدام رائع للبحث + الاستبدال في XML؟

StackOverflow https://stackoverflow.com/questions/91957

  •  01-07-2019
  •  | 
  •  

سؤال

كيف يمكنني استخدام رائع للبحث + الاستبدال في XML؟

أحتاج إلى شيء قصير/سهل قدر الإمكان، لأنني سأعطي هذا الرمز للمختبرين من أجل البرمجة النصية لـ SoapUI الخاصة بهم.

وبشكل أكثر تحديدًا، كيف يمكنني أن أتحول:

<root><data></data></root>

داخل:

<root><data>value</data></root>
هل كانت مفيدة؟

المحلول

بعض الأشياء التي يمكنك القيام بها باستخدام XSLT يمكنك أيضًا القيام بها باستخدام أحد أشكال "البحث والاستبدال".كل هذا يتوقف على مدى تعقيد مشكلتك ومدى "عامة" الحل الذي تريد تنفيذه.لجعل المثال الخاص بك أكثر عمومية قليلاً:

xml.replaceFirst("<Mobiltlf>[^<]*</Mobiltlf>", '<Mobiltlf>32165487</Mobiltlf>')

الحل الذي تختاره متروك لك.في تجربتي الخاصة (للمشاكل البسيطة جدًا) يعد استخدام عمليات البحث البسيطة عن السلاسل أسرع من استخدام التعبيرات العادية التي تعد أسرع مرة أخرى من استخدام تحويل XSLT الكامل (وهو أمر منطقي في الواقع).

نصائح أخرى

بعد بعض الترميز المحموم رأيت النور وفعلت هذا

import org.custommonkey.xmlunit.Diff
import org.custommonkey.xmlunit.XMLUnit

def input = '''<root><data></data></root>'''
def expectedResult = '''<root><data>value</data></root>'''

def xml = new XmlParser().parseText(input)

def p = xml.'**'.data
p.each{it.value="value"}

def writer = new StringWriter()
new XmlNodePrinter(new PrintWriter(writer)).print(xml)
def result = writer.toString()

XMLUnit.setIgnoreWhitespace(true)
def xmlDiff = new Diff(result, expectedResult)
assert xmlDiff.identical()

لسوء الحظ، لن يحافظ هذا على التعليقات والبيانات التعريفية وما إلى ذلك، من مستند XML الأصلي، لذا سأضطر إلى إيجاد طريقة أخرى

لقد أجريت بعض الاختبارات باستخدام DOMCategory وهي تعمل تقريبًا.يمكنني إجراء الاستبدال، لكن بعض التعليقات المتعلقة بمسار المعلومات تختفي.أنا أستخدم طريقة مثل هذا:

def rtv = { xml, tag, value ->
    def doc     = DOMBuilder.parse(new StringReader(xml))
    def root    = doc.documentElement
    use(DOMCategory) { root.'**'."$tag".each{it.value=value} }
    return DOMUtil.serialize(root)    
}

على مصدر مثل هذا:

<?xml version="1.0" encoding="utf-8"?>
<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:FA_Ansoegning:http---ementor-dk-application-2007-06-22-" href="manifest.xsf" solutionVersion="1.0.0.14" productVersion="12.0.0" PIVersion="1.0.0.0" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<application:FA_Ansoegning xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:application="http://corp.dk/application/2007/06/22/"
xmlns:xd="http://schemas.microsoft.com/office/infopath/2003"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/200    8-04-14T14:31:48">
    <Mobiltlf></Mobiltlf>
  <E-mail-adresse></E-mail-adresse>
</application:FA_Ansoegning>

الشيء الوحيد المفقود من النتيجة هو <?mso- الأسطر من النتيجة.أي شخص لديه فكرة عن ذلك؟

هذه هي أفضل إجابة حتى الآن وهي تعطي النتيجة الصحيحة ، لذلك سأقبل الإجابة :) ومع ذلك ، فهي كبيرة جدًا بالنسبة لي.أعتقد أنه من الأفضل أن أشرح أن البديل هو:

xml.replace("<Mobiltlf></Mobiltlf>", <Mobiltlf>32165487</Mobiltlf>")

لكن هذا ليس xml'y جدًا لذا اعتقدت أنني سأبحث عن بديل.كما لا يمكنني التأكد من أن العلامة الأولى فارغة طوال الوقت.

للاحتفاظ بالسمات، ما عليك سوى تعديل برنامجك الصغير مثل هذا (لقد قمت بتضمين نموذج مصدر لاختباره):

def input = """
<?xml version="1.0" encoding="utf-8"?>
<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:FA_Ansoegning:http---ementor-dk-application-2007-06-22-" href="manifest.xsf" solutionVersion="1.0.0.14" productVersion="12.0.0" PIVersion="1.0.0.0" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<application:FA_Ansoegning xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:application="http://ementor.dk/application/2007/06/22/"
xmlns:xd="http://schemas.microsoft.com/office/infopath/2003"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/200    8-04-14T14:31:48">
    <Mobiltlf  type="national" anotherattribute="value"></Mobiltlf>
  <E-mail-adresse attr="whatever"></E-mail-adresse>
</application:FA_Ansoegning>
""".trim()

def rtv = { xmlSource, tagName, newValue ->
    regex = "(<$tagName[^>]*>)([^<]*)(</$tagName>)"
    replacement = "\$1${newValue}\$3"
    xmlSource = xmlSource.replaceAll(regex, replacement)
    return xmlSource
}

input = rtv( input, "Mobiltlf", "32165487" )
input = rtv( input, "E-mail-adresse", "bob@email.com" )
println input

تشغيل هذا البرنامج النصي ينتج:

<?xml version="1.0" encoding="utf-8"?>
<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:FA_Ansoegning:http---ementor-dk-application-2007-06-22-" href="manifest.xsf" solutionVersion="1.0.0.14" productVersion="12.0.0" PIVersion="1.0.0.0" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<application:FA_Ansoegning xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:application="http://ementor.dk/application/2007/06/22/"
xmlns:xd="http://schemas.microsoft.com/office/infopath/2003"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/200    8-04-14T14:31:48">
    <Mobiltlf  type="national" anotherattribute="value">32165487</Mobiltlf>
  <E-mail-adresse attr="whatever">bob@email.com</E-mail-adresse>
</application:FA_Ansoegning>

لاحظ أن التعبير العادي المطابق يحتوي الآن على ثلاث مجموعات التقاط:(1) علامة البداية (بما في ذلك السمات)، (2) أيًا كان المحتوى "القديم" لعلامتك و(3) علامة النهاية.تشير السلسلة البديلة إلى هذه المجموعات الملتقطة عبر بناء الجملة $i (مع خطوط مائلة عكسية للهروب منها في GString).مجرد غيض:التعبيرات النمطية هي حيوانات قوية جدًا، ومن المفيد حقًا أن تتعرف عليها؛-) .

تم وصف ثلاث طرق "رسمية" رائعة لتحديث XML في الصفحة http://groovy.codehaus.org/Processing+XML, قسم "تحديث XML".

من بين هؤلاء الثلاثة يبدو أن طريقة DOMCategory فقط هي التي تحافظ على تعليقات XML وما إلى ذلك.

بالنسبة لي، يبدو أن النسخ الفعلي والبحث والاستبدال هو الوظيفة المثالية لورقة أنماط XSLT.في XSLT، ليس لديك أي مشكلة على الإطلاق في نسخ كل شيء (بما في ذلك العناصر التي تواجه مشكلات معها) وإدراج بياناتك ببساطة حيثما تكون مطلوبة.يمكنك تمرير القيمة المحددة لبياناتك عبر معلمة XSL أو يمكنك تعديل ورقة الأنماط نفسها ديناميكيًا (إذا قمت بتضمينها كسلسلة في برنامج Groovy الخاص بك).يعد استدعاء XSLT لتحويل مستندك (مستنداتك) من داخل Groovy أمرًا بسيطًا للغاية.

لقد قمت بسرعة بتجميع نص Groovy التالي معًا (ولكن ليس لدي أدنى شك في أنه يمكن كتابته بشكل أكثر بساطة/إحكاما):

import javax.xml.transform.TransformerFactory
import javax.xml.transform.stream.StreamResult
import javax.xml.transform.stream.StreamSource

def xml = """
<?xml version="1.0" encoding="utf-8"?>
<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:FA_Ansoegning:http---ementor-dk-application-2007-06-22-" href="manifest.xsf" solutionVersion="1.0.0.14" productVersion="12.0.0" PIVersion="1.0.0.0" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<application:FA_Ansoegning xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:application="http://ementor.dk/application/2007/06/22/"
xmlns:xd="http://schemas.microsoft.com/office/infopath/2003"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/200    8-04-14T14:31:48">
    <Mobiltlf></Mobiltlf>
  <E-mail-adresse></E-mail-adresse>
</application:FA_Ansoegning>
""".trim()

def xslt = """
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:param name="mobil" select="'***dummy***'"/>
    <xsl:param name="email" select="'***dummy***'"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Mobiltlf">
        <xsl:copy>
            <xsl:value-of select="\$mobil"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="E-mail-adresse">
        <xsl:copy>
            <xsl:value-of select="\$email"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
""".trim()

def factory = TransformerFactory.newInstance()
def transformer = factory.newTransformer(new StreamSource(new StringReader(xslt)))

transformer.setParameter('mobil', '1234567890')
transformer.setParameter('email', 'john.doe@foobar.com')

transformer.transform(new StreamSource(new StringReader(xml)), new StreamResult(System.out))

تشغيل هذا البرنامج النصي ينتج:

<?xml version="1.0" encoding="UTF-8"?><?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:FA_Ansoegning:http---ementor-dk-application-2007-06-22-" href="manifest.xsf" solutionVersion="1.0.0.14" productVersion="12.0.0" PIVersion="1.0.0.0" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<application:FA_Ansoegning xmlns:application="http://ementor.dk/application/2007/06/22/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/200    8-04-14T14:31:48">
    <Mobiltlf>1234567890</Mobiltlf>
  <E-mail-adresse>john.doe@foobar.com</E-mail-adresse>
</application:FA_Ansoegning>

باهِر!شكرا جزيلا لمساعدتكم :)

هذا يحل مشكلتي بطريقة أكثر نظافة وأسهل.لقد انتهى الأمر بهذا الشكل:

def rtv = { xmlSource, tagName, newValue ->
    regex = "<$tagName>[^<]*</$tagName>"
    replacement = "<$tagName>${newValue}</$tagName>"
    xmlSource = xmlSource.replaceAll(regex, replacement)
    return xmlSource
}

input = rtv( input, "Mobiltlf", "32165487" )
input = rtv( input, "E-mail-adresse", "bob@email.com" )
println input

نظرًا لأنني أعطي هذا للمختبرين لدينا لاستخدامه في أداة الاختبار الخاصة بهم SoapUI، فقد حاولت "تغليفه"، لتسهيل النسخ واللصق عليهم.

وهذا أمر جيد بما يكفي لتحقيق هدفي، ولكنه سيكون مثاليًا إذا تمكنا من إضافة "تطور" آخر.

لنفترض أن المدخلات كانت تحتوي على هذا ...

<Mobiltlf type="national" anotherattribute="value"></Mobiltlf>

... وأردنا الاحتفاظ بهاتين السمتين على الرغم من أننا استبدلنا القيمة.هل هناك طريقة لاستخدام regexp لذلك أيضًا؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top