如何自定义Ruby的元帅负载而不更改格式?

发布于 2025-01-18 09:25:45 字数 597 浏览 1 评论 0原文

我需要自定义 Marshal.load() 上我的一个类发生的情况。我发现有 marshal_load_load 方法,但到目前为止我还没有成功地使用它们中的任何一个来满足我的需要。

对我来说至关重要的是,与未定义这些方法相比,生成的二进制数据不会发生更改。这排除了 _dump_load,因为它们用于定义自定义序列化字符串格式。

我尝试使用 marshal_load 和 marshal_dump 但它也改变了生成的二进制格式。这不可能发生。

编辑:为了更具体地说明我的用例,我有一个包含 8 位字符串的编组二进制文件。我需要在 marshal_load 中将这些字符串更改为 utf8,并在 marshal_dump 中将其更改回 8 位。 我创建了这个存储库用于测试,请阅读此处的评论: https://github.com/enumag/marshal-test/blob/主控/测试.rb

I need to customize what happens to one of my class on Marshal.load(). I found that there are marshal_load and _load methods but so far I was unsuccessful to use either of them to what I need.

It's crucial for me that the resulting binary data are not changed compared to if those methods are not defined. This rules out _dump and _load as they're for defining custom serialization string format.

I tried to use marshal_load and marshal_dump but it changes the resulting binary format as well. This can't happen.

EDIT: To be a bit more specific about my usecase I have a marshalled binary file which contains strings in 8bit. I need to change these strings to utf8 in marshal_load and back to 8bit in marshal_dump.
I created this repository for testing, read the comments here:
https://github.com/enumag/marshal-test/blob/master/test.rb

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦言归人 2025-01-25 09:25:45

我在您的github示例中看到了您有以下评论,但是我认为您没有选择在转储前和加载后不手动进行。

实际上,我正在元帅加载带有许多嵌套物体的大结构,所以我不能手动做

我建议以下两个方案之一。

创建围绕元帅的包装器,并使用它在安排之前和之后修改对象。

class Foo
    def initialize(attr_1, attr_2)
        @attr_1 = attr_1
        @attr_2 = attr_2
    end
end

class MyMarshaler
    def load(file_name)
        source = File.new(file_name, "r")
        loaded = Marshal.load(source)
        source.close
        # do whatever you want with loaded object
        manipulated = loaded     
    end

    def dump(obj, file_name)
        dest = File.new(file_name, "w")
        # do whatever you want with object before dumping it
        manipulated = obj
        Marshal.dump(manipulated, dest)
        dest.close
    end
end

foo = Foo.new("foo-thing-1", "foo-thing-2")
foo_file_name = "marshaled.ruby_object"
marshaler = MyMarshaler.new
p foo
marshaler.dump(foo, foo_file_name)
restored_foo = marshaler.load(foo_file_name)
p restored_foo

另一个选项是创建一个模块并扩展您的类,以包括包装元帅/转储方法的方法。

module MyMarshalerModule
    def load(file_name)
        source = File.new(file_name, "r")
        loaded = Marshal.load(source)
        source.close
        # do whatever you want with loaded object
        manipulated = loaded     
    end

    def dump(obj, file_name)
        dest = File.new(file_name, "w")
        # do whatever you want with object before dumping it
        manipulated = obj
        Marshal.dump(manipulated, dest)
        dest.close
    end
end

class Bar
    extend MyMarshalerModule
    def initialize(attr_1, attr_2)
        @attr_1 = attr_1
        @attr_2 = attr_2
    end
end

bar = Bar.new("bar-thing-1", "bar-thing-2")
bar_file_name = "marshaled2.ruby_object"
p bar
Bar.dump(bar, bar_file_name)
restored_bar = Bar.load(bar_file_name)
p restored_bar

I saw in your Github example you have the below comment, but I don't think you have the option not to do it manually before the dump and after the load.

in practice I'm Marshal loading a large structure with many nested objects so I can't do it manually

I would suggest one of the two following schemes.

Create wrapper around Marshal and use that to modify your object before and after marshaling.

class Foo
    def initialize(attr_1, attr_2)
        @attr_1 = attr_1
        @attr_2 = attr_2
    end
end

class MyMarshaler
    def load(file_name)
        source = File.new(file_name, "r")
        loaded = Marshal.load(source)
        source.close
        # do whatever you want with loaded object
        manipulated = loaded     
    end

    def dump(obj, file_name)
        dest = File.new(file_name, "w")
        # do whatever you want with object before dumping it
        manipulated = obj
        Marshal.dump(manipulated, dest)
        dest.close
    end
end

foo = Foo.new("foo-thing-1", "foo-thing-2")
foo_file_name = "marshaled.ruby_object"
marshaler = MyMarshaler.new
p foo
marshaler.dump(foo, foo_file_name)
restored_foo = marshaler.load(foo_file_name)
p restored_foo

The other option would be to create a Module and extend your class to include methods that wrap the marshal load/dump methods.

module MyMarshalerModule
    def load(file_name)
        source = File.new(file_name, "r")
        loaded = Marshal.load(source)
        source.close
        # do whatever you want with loaded object
        manipulated = loaded     
    end

    def dump(obj, file_name)
        dest = File.new(file_name, "w")
        # do whatever you want with object before dumping it
        manipulated = obj
        Marshal.dump(manipulated, dest)
        dest.close
    end
end

class Bar
    extend MyMarshalerModule
    def initialize(attr_1, attr_2)
        @attr_1 = attr_1
        @attr_2 = attr_2
    end
end

bar = Bar.new("bar-thing-1", "bar-thing-2")
bar_file_name = "marshaled2.ruby_object"
p bar
Bar.dump(bar, bar_file_name)
restored_bar = Bar.load(bar_file_name)
p restored_bar
水波映月 2025-01-25 09:25:45

我认为我的原始答案在您的帖子中解决了问题,但是在评论中,您暗示您确实有2个问题。

这个答案试图回答两个问题。

这是一个透射类,可让您将块传递到负载和转储方法,并递归地寻找类的实例,并使用嵌套块转换了这些实例。转换的对象未触及。

class TransMarshal
    def self.load(file_name)
        source = File.new(file_name, "r")
        loaded = Marshal.load(source)
        source.close
        block_given? ? yield(loaded) : loaded
    end
    
    def self.dump(obj, file_name)
        dest = File.new(file_name, "w")
        manipulated = block_given? ? yield(obj) : obj
        Marshal.dump(manipulated, dest)
        dest.close
    end

    def self.transform(obj, klass, &block)
        cloned_obj = obj.clone
        return cloned_obj if obj.instance_variables.empty?
        cloned_obj.instance_variables.each do |o|
            cloned_inst_var = cloned_obj.instance_variable_get(o).clone
            cloned_obj.instance_variable_set(o, transform(cloned_inst_var, klass, &block))
            if cloned_inst_var.class == klass
                cloned_obj.instance_variable_set(o, yield(cloned_inst_var))
            end
        end
        cloned_obj
    end

end

示例用法。

class Foo
    def initialize(attr_1, attr_2)
        @attr_1 = attr_1
        @attr_2 = attr_2
    end
end

class Bar
    def initialize(attr_1, attr_2)
        @attr_1 = attr_1
        @attr_2 = attr_2
    end
end

class FooBar
    # extend MyMarshalerModule
    def initialize(foo, bar, string, number)
        @foo = foo
        @bar = bar
        @string = string
        @number = number
    end 
end

foo = Foo.new("foo_attr_1", "foo_attr_2")
bar = Bar.new("bar_attr_1", "bar_attr_2")
foo_bar = FooBar.new(foo, bar, "foobar", 42)

TransMarshal.dump(foo_bar, "dumped") do |obj|
    TransMarshal.transform(obj, String) {|s| "#{s}-x"}
end

raw_restored_foo_bar = TransMarshal.load("dumped")
puts "raw_restored_foo_bar = #{raw_restored_foo_bar.inspect}"

transformed_restored_foo_bar = TransMarshal.load("dumped") do |obj|
     TransMarshal.transform(obj, String) { |o| o[0..-3] }
end
puts "transformed_restored_foo_bar = #{transformed_restored_foo_bar.inspect}"

I think my original answer addressed the question in your post, but in the comments you imply that you really have 2 questions.

This answer attempts to answer both questions.

Here is a TransMarshal class that allows you to pass a block to both the load and dump methods that recursively looks for instances of a class and transform those instances using a nested block. The objects of the transformation are untouched.

class TransMarshal
    def self.load(file_name)
        source = File.new(file_name, "r")
        loaded = Marshal.load(source)
        source.close
        block_given? ? yield(loaded) : loaded
    end
    
    def self.dump(obj, file_name)
        dest = File.new(file_name, "w")
        manipulated = block_given? ? yield(obj) : obj
        Marshal.dump(manipulated, dest)
        dest.close
    end

    def self.transform(obj, klass, &block)
        cloned_obj = obj.clone
        return cloned_obj if obj.instance_variables.empty?
        cloned_obj.instance_variables.each do |o|
            cloned_inst_var = cloned_obj.instance_variable_get(o).clone
            cloned_obj.instance_variable_set(o, transform(cloned_inst_var, klass, &block))
            if cloned_inst_var.class == klass
                cloned_obj.instance_variable_set(o, yield(cloned_inst_var))
            end
        end
        cloned_obj
    end

end

Example usage.

class Foo
    def initialize(attr_1, attr_2)
        @attr_1 = attr_1
        @attr_2 = attr_2
    end
end

class Bar
    def initialize(attr_1, attr_2)
        @attr_1 = attr_1
        @attr_2 = attr_2
    end
end

class FooBar
    # extend MyMarshalerModule
    def initialize(foo, bar, string, number)
        @foo = foo
        @bar = bar
        @string = string
        @number = number
    end 
end

foo = Foo.new("foo_attr_1", "foo_attr_2")
bar = Bar.new("bar_attr_1", "bar_attr_2")
foo_bar = FooBar.new(foo, bar, "foobar", 42)

TransMarshal.dump(foo_bar, "dumped") do |obj|
    TransMarshal.transform(obj, String) {|s| "#{s}-x"}
end

raw_restored_foo_bar = TransMarshal.load("dumped")
puts "raw_restored_foo_bar = #{raw_restored_foo_bar.inspect}"

transformed_restored_foo_bar = TransMarshal.load("dumped") do |obj|
     TransMarshal.transform(obj, String) { |o| o[0..-3] }
end
puts "transformed_restored_foo_bar = #{transformed_restored_foo_bar.inspect}"
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文