【问题标题】:from_json doesn't work with record macro?from_json 不适用于记录宏?
【发布时间】:2021-08-09 20:42:20
【问题描述】:

为什么from_json 不适用于使用record 宏创建的struct?

require "json"

record Stock,
  symbol :           String,
  name :             String

p Stock.from_json %({ "symbol": "MSFT", "name": "Some" })

错误

 13 | new parser
      ^--
Error: wrong number of arguments for 'Stock.new' (given 1, expected 2)

Overloads are:
 - Stock.new(symbol : String, name : String)

问题 2:

堆栈跟踪没有行号,我尝试使用--error-trace但没有效果,我该如何使用--error-trace?

> crystal api/crystal/play.cr --error-trace
Showing last frame. Use --error-trace for full trace.

In /crystal-1.1.1-1/src/json/from_json.cr:13:3

 13 | new parser
      ^--
Error: wrong number of arguments for 'Stock.new' (given 1, expected 2)

Overloads are:
 - Stock.new(symbol : String, name : String)

附言

我找到了解决方案,虽然如果它只是工作会更好,不需要包含JSON::Serializable

record Stock,
  symbol :           String,
  name :             String,
do
  include JSON::Serializable
end

【问题讨论】:

    标签: crystal-lang


    【解决方案1】:

    你已经明白了。关于是否应始终包含 JSON::Serialize 的问题,这意味着 JSON 支持可用 (require "json")。否则,它不会编译。我认为这就是默认情况下它没有被烘焙的原因。

    关于原始问题,有一种调试宏的方法。如果在宏末尾包含{% debug %},它将打印生成的代码。我在您的示例中通过复制record macro 的来源(来源为here)进行了尝试,仅重命名recordby my_record:

    macro my_record(name, *properties)
      struct {{name.id}}
        {% for property in properties %}
          {% if property.is_a?(Assign) %}
            getter {{property.target.id}}
          {% elsif property.is_a?(TypeDeclaration) %}
            getter {{property}}
          {% else %}
            getter :{{property.id}}
          {% end %}
        {% end %}
    
        def initialize({{
                         *properties.map do |field|
                           "@#{field.id}".id
                         end
                       }})
        end
    
        {{yield}}
    
        def copy_with({{
                        *properties.map do |property|
                          if property.is_a?(Assign)
                            "#{property.target.id} _#{property.target.id} = @#{property.target.id}".id
                          elsif property.is_a?(TypeDeclaration)
                            "#{property.var.id} _#{property.var.id} = @#{property.var.id}".id
                          else
                            "#{property.id} _#{property.id} = @#{property.id}".id
                          end
                        end
                      }})
          self.class.new({{
                           *properties.map do |property|
                             if property.is_a?(Assign)
                               "_#{property.target.id}".id
                             elsif property.is_a?(TypeDeclaration)
                               "_#{property.var.id}".id
                             else
                               "_#{property.id}".id
                             end
                           end
                         }})
        end
    
        def clone
          self.class.new({{
                           *properties.map do |property|
                             if property.is_a?(Assign)
                               "@#{property.target.id}.clone".id
                             elsif property.is_a?(TypeDeclaration)
                               "@#{property.var.id}.clone".id
                             else
                               "@#{property.id}.clone".id
                             end
                           end
                         }})
        end
      end
      {% debug %}
    end
    

    注意最后的{% debug %}。现在在运行您的示例时...

    my_record Stock,
      symbol :           String,
      name :             String
    

    ...它扩展为:

    struct Stock
      getter symbol : String
    
      getter name : String
    
      def initialize(@symbol : String, @name : String)
      end
    
      def copy_with(symbol _symbol = @symbol, name _name = @name)
        self.class.new(_symbol, _name)
      end
    
      def clone
        self.class.new(@symbol.clone, @name.clone)
      end
    end
    

    另外,--error-trace 参数应该可以工作。我没有用crystal play试过,但是你可以用crystal run。该选项必须放在文件名之前:

    $ crystal run --error-trace example.cr
    struct Stock
      getter symbol : String
    
      getter name : String
    
      def initialize(@symbol : String, @name : String)
      end
    
      def copy_with(symbol _symbol = @symbol, name _name = @name)
        self.class.new(_symbol, _name)
      end
    
      def clone
        self.class.new(@symbol.clone, @name.clone)
      end
    end
    In example.cr:70:9
    
     70 | p Stock.from_json %({ "symbol": "MSFT", "name": "Some" })
                  ^--------
    Error: instantiating 'Stock.class#from_json(String)'
    
    
    In /usr/lib/crystal/json/from_json.cr:13:3
    
     13 | new parser
          ^--
    Error: wrong number of arguments for 'Stock.new' (given 1, expected 2)
    
    Overloads are:
     - Stock.new(symbol : String, name : String)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-15
      • 1970-01-01
      • 2022-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多