ruby - XML لتحويل التجزئة: يقوم Nori بإسقاط سمات عناصر XML الأكثر عمقًا



hash (1)

نوري في الواقع لا تسقط الصفات ، لا يتم طباعتها.

إذا قمت بتشغيل البرنامج النصي ruby:

require 'nori'

data = Nori.new(empty_tag_value: true).parse(<<XML)
<?xml version="1.0"?>
<root>
  <objects>
    <object>
      <fields>
        <field name="Name">The name</field>
        <field name="Description">A description</field>
      </fields>
    </object>
  </objects>
</root>
XML

field_list = data['root']['objects']['object']['fields']['field']

puts "text: '#{field_list[0]}' data: #{field_list[0].attributes}"
puts "text: '#{field_list[1]}' data: #{field_list[1].attributes}"

يجب عليك الحصول على الإخراج

["The name", "A description"]
text: 'The name' data: {"name"=>"Name"}
text: 'A description' data: {"name"=>"Description"}

والذي يوضح بوضوح أن السمة موجودة ، ولكن لا يتم عرضها بواسطة طريقة inspect (تكون وظيفة p(x) هي نفسها التي puts x.inspect ).

ستلاحظ أن puts field_list.inspect المخرجات puts field_list.inspect ["The name", "A description"] . لكن field_list[0].attributes السمات مفتاح السمة والبيانات.

إذا كنت ترغب في عرض pp هذا ، يمكنك تحميل أسلوب inspect في Nori::StringWithAttributes .

class Nori
  class StringWithAttributes < String
    def inspect
      [attributes, String.new(self)].inspect
    end
  end
end

أو إذا كنت ترغب في تغيير الإخراج ، يمكنك تحميل أسلوب self.new إلى مقطع بيانات مختلف.

class Nori
  class MyText < Array
    def attributes=(data)
      self[1] = data
    end
    attr_accessor :text
    def initialize(text)
      self[0] = text
      self[1] = {}
    end
  end
  class StringWithAttributes < String
    def self.new(x)
      MyText.new(x)
    end
  end
end

والوصول إلى البيانات باعتبارها صف

puts "text: '#{data['root']['objects']['object']['fields']['field'][0].first}' data: #{ data['root']['objects']['object']['fields']['field'][0].last}"

هذا من شأنه أن يجعل الأمر كذلك حتى تتمكن من الحصول على البيانات مثل JSON أو YAML حيث ستبدو العناصر النصية مثل المصفوفة مع عنصرين. يعمل pp أيضا.

{"root"=>
  {"objects"=>
    {"object"=>
      {"fields"=>
        {"field"=>
          [["The name", {"name"=>"Name"}],
           ["A description", {"name"=>"Description"}]]},
       "bob"=>[{"@id"=>"id1"}, {"@id"=>"id2"}],
       "bill"=>
        [{"p"=>["one", {}], "@id"=>"bid1"}, {"p"=>["two", {}], "@id"=>"bid2"}],
       "@id"=>"1"}}}}

هذا يجب أن يفعل ما تريد.

require 'awesome_print'
require 'nori'

# Copyright (c) 2016 G. Allen Morris III
#
# Awesome Print is freely distributable under the terms of MIT license.
# See LICENSE file or http://www.opensource.org/licenses/mit-license.php
#------------------------------------------------------------------------------
module AwesomePrint
  module Nori

    def self.included(base)
      base.send :alias_method, :cast_without_nori, :cast
      base.send :alias_method, :cast, :cast_with_nori
    end

    # Add Nori XML Node and NodeSet names to the dispatcher pipeline.
    #-------------------------------------------------------------------
    def cast_with_nori(object, type)
      cast = cast_without_nori(object, type)
      if defined?(::Nori::StringWithAttributes) && object.is_a?(::Nori::StringWithAttributes)
        cast = :nori_xml_node
      end
      cast
    end

    #-------------------------------------------------------------------
    def awesome_nori_xml_node(object)
      return %Q|["#{object}", #{object.attributes}]|
    end
  end
end

AwesomePrint::Formatter.send(:include, AwesomePrint::Nori)

data = Nori.new(empty_tag_value: true).parse(<<XML)
<?xml version="1.0"?>
<root>
  <objects>
    <object>
      <fields>
        <field name="Name">The name</field>
        <field name="Description">A description</field>
      </fields>
    </object>
  </objects>
</root>
XML

ap data

كما هو الإخراج:

{
    "root" => {
        "objects" => {
            "object" => {
                "fields" => {
                    "field" => [
                        [0] ["The name", {"name"=>"Name"}],
                        [1] ["A description", {"name"=>"Description"}]
                    ]
                }
            }
        }
    }
}

ملخص

أنا استخدم Ruby ( ruby 2.1.2p95 (2014-05-08) [x86_64-linux-gnu] على الجهاز الخاص بي ، ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux] في بيئة الإنتاج) ونوري لتحويل مستند XML (تم معالجته في البداية مع Nokogiri لبعض التحقق من الصحة) في Ruby Hash ، لكنني اكتشفت لاحقا أن Nori تقوم بإسقاط سمات أعمق عناصر XML.

تفاصيل المشكلة واعادة انتاجها

لتنفيذ ذلك ، أستخدم رمزًا مشابهًا لما يلي:

xml  = Nokogiri::XML(File.open('file.xml')) { |config| config.strict.noblanks }
hash = Nori.new.parse xml.to_s

يعمل الرمز بشكل عام على النحو المنشود ، باستثناء حالة واحدة. عندما يوزع نوري نص XML ، فإنه يسقط سمات العناصر من عناصر ورقة (أي العناصر التي ليس لها عناصر تابعة).

على سبيل المثال ، المستند التالي:

<?xml version="1.0"?>
<root>
  <objects>
    <object>
      <fields>
        <id>1</id>
        <name>The name</name>
        <description>A description</description>
      </fields>
    </object>
  </objects>
</root>

... يتم تحويله إلى التجزئة المتوقعة (تم حذف بعض الإخراج للإيجاز):

irb(main):066:0> xml = Nokogiri::XML(txt) { |config| config.strict.noblanks }
irb(main):071:0> ap Nori.new.parse(xml.to_s), :indent => -2
{
  "root" => {
    "objects" => {
      "object" => {
        "fields" => {
          "id"   => "1",
          "name" => "The name"
          "description" => "A description"
        }
      }
    }
  }
}

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

<?xml version="1.0"?>
<root>
  <objects>
    <object id="1">
      <fields>
        <field name="Name">The name</field>
        <field name="Description">A description</field>
      </fields>
    </object>
  </objects>
</root>

نفس Nori.new.parse(xml.to_s) ، كما هو معروض في awesome_print ، يظهر سمات أعمق عناصر <field> غير موجودة :

irb(main):131:0> ap Nori.new.parse(xml.to_s), :indent => -2
{
  "root" => {
    "objects" => {
      "object" => {
        "fields" => {
          "field" => [
            [0] "The name",
            [1] "A description"
          ]
        },
        "@id"    => "1"
      }
    }
  }
}

التجزئة لها قيمها فقط كقائمة ، وهذا ليس ما أردت. توقعت أن تحتفظ عناصر <field> بسماتها مثل عناصرها الأصلية (على سبيل المثال ، انظر @id="1" لـ <object> ) ، وليس لخصائصها.

حتى إذا تم تعديل المستند ليبدو كما يلي ، فإنه لا يعمل كما هو متوقع:

<?xml version="1.0"?>
<root>
  <objects>
    <object id="1">
      <fields>
        <Name type="string">The name</Name>
        <Description type="string">A description</Description>
      </fields>
    </object>
  </objects>
</root>

ينتج عن التجزئة التالية:

{
  "root" => {
    "objects" => {
      "object" => {
        "fields" => {
          "Name"        => "The name",
          "Description" => "A description"
        },
        "@id"    => "1"
      }
    }
  }
}

التي تفتقر إلى السمات type="whatever" لكل إدخال حقل.

يقودني البحث في النهاية إلى الإصدار رقم 59 مع المشاركة الأخيرة (من أغسطس 2015) ، حيث يشير إلى أنه لا يمكنه "العثور على الخطأ في شفرة نوري".

استنتاج

لذا ، سؤالي هو: هل أي واحد منكم على بينة من طريقة للعمل حول قضية نوري (على سبيل المثال ربما إعداد) التي تسمح لي باستخدام مخطط بلدي الأصلي (أي واحد مع سمات في العناصر مع الأطفال)؟ إذا كان الأمر كذلك ، فهل يمكنك مشاركة مقتطف شفرة يمكنه التعامل مع هذا بشكل صحيح؟

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

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

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





hash