带有varargs的case类的隐式jsonFormat

时间:2015-01-29 10:16:50

标签: scala akka spray spray-json

我有一个包含varargs的case类,隐含的jsonFormat如下:

import spray.json._
case class Colors(name: String*)
object MyJsonProtocol extends DefaultJsonProtocol {
  implicit val colorFormat = jsonFormat1(Colors)
}
import MyJsonProtocol._
Colors("CadetBlue").toJson

它引发了一个错误:

error: type mismatch;
found   : Color2.type
required: Seq[String] => Color2
Note: implicit value colorFormat is not applicable here because it comes after the application point and it lacks an explicit result type
      implicit val colorFormat = jsonFormat1(Color2)
                                            ^

我也尝试过:

implicit val colorFormat = jsonFormat1(Colors.apply)

导致了一个不同的(运行时!)异常:

java.lang.RuntimeException: Cannot automatically determine case class field names and order for 'Colors', please use the 'jsonFormat' overload with explicit field name specification

以下内容:

implicit val colorFormat = jsonFormat(Colors, "name")

提出了前一个错误

甚至可以使用varargs为案例类定义隐式jsonFormat?

3 个答案:

答案 0 :(得分:7)

我也遇到了此异常,但是在我的情况下,这是由于case类中的“ val”定义(而不是作为传递的参数)。

问题案例类别:

<script>
$(document).ready(function(){

  tinymce.init({
    selector: '#post-content',
    height: 380,
    plugins: 'save print preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template table charmap hr pagebreak nonbreaking anchor toc insertdatetime advlist lists textcolor wordcount imagetools contextmenu colorpicker textpattern help',
    toolbar: 'save | formatselect | bold italic strikethrough forecolor backcolor | link | alignleft aligncenter alignright alignjustify | numlist bullist outdent indent | removeformat | image',
    images_upload_url: 'ImageHandler.php',
    automatic_uploads: false,
    save_onsavecallback: function(){
        var action = 'savePost';
        var dataTitle = $('#clientName').val();
        var data = tinymce.get('post-content').getContent();
        $.ajax({
          type: 'POST',
          url: 'PostHandler.php',
          data: {action: action, client_name: dataTitle, post_content: data},
          cache: false,
          success: function(response){
            alert(response);
          }
        });
    }
  });

});

</script> 

有效案例类:

<?php

  $accepted_origins = array("http://localhost", "http://192.168.1.1", "http://example.com");

  $imageFolder = "../../img/portfolio/";

  reset ($_FILES);
  $temp = current($_FILES);
  if (is_uploaded_file($temp['tmp_name'])){

    // if (isset($_SERVER['HTTP_ORIGIN'])) {
    //   // same-origin requests won't set an origin. If the origin is set, it must be valid.
    //   if (in_array($_SERVER['HTTP_ORIGIN'], $accepted_origins)) {
    //     header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
    //   } else {
    //     header("HTTP/1.1 403 Origin Denied");
    //     return;
    //   }
    // }

    if (preg_match("/([^\w\s\d\-_~,;:\[\]\(\).])|([\.]{2,})/", $temp['name'])) {
        header("HTTP/1.1 400 Invalid file name.");
        return;
    }

    // Verify extension
    if (!in_array(strtolower(pathinfo($temp['name'], PATHINFO_EXTENSION)), array("gif", "jpg", "png"))) {
        header("HTTP/1.1 400 Invalid extension.");
        return;
    }

    if(!file_exists($_POST['client_name'])){
      $clientFolder = mkdir($imageFolder.$_POST['client_name'], 0777);
    }
    // Accept upload if there was no origin, or if it is an accepted origin
    //$filetowrite = $imageFolder . $temp['name'];
    $filetowrite = $clientFolder . $temp['name'];

    move_uploaded_file($temp['tmp_name'], $filetowrite);

    // Respond to the successful upload with JSON.
    // Use a location key to specify the path to the saved image resource.
    // { location : '/your/uploaded/image/file'}


    echo json_encode(array('location' => $filetowrite));

  } else {
    // Notify editor that the upload failed
    header("HTTP/1.1 500 Server Error");
  }

?>

这有点不幸,因为现在将在每次调用时计算“代码”。

(使用了Akka Http版本-10.1.1)

答案 1 :(得分:5)

它应该完美地工作,可能你有一些含糊不清的含义。这非常有效:

import spray.json._, DefaultJsonProtocol._

case class Test(param: String*)
object Test {
  implicit val testFormat = jsonFormat1(Test.apply)
}

就像最佳实践建议一样,不要使用协议模式,它会导致大项目中的大隐式错误,总是在伴随对象中定义隐含(因为有异常情况)。另一点是避免继承,它并不是真正需要的。

Scala *模式只是Seq类型构造函数的糖,所以它应该为这种情况找到seqFormat(un)marshaller。

<强>更新

它不起作用,因为Spray使用ClassManifestcopy函数中提取字段名称,但编译器在构造函数中没有为带有varargs的case类生成此函数:

case class Test(params: String*)
def man[A: ClassManifest] = implicitly[ClassManifest[A]]
man[Test].erasure.getDeclaredMethods.filter(_.getName.contains("copy"))
res4: Array[java.lang.reflect.Method] = Array()

scala> case class Test(param: String)
defined class Test

scala> man[Test].erasure.getDeclaredMethods.filter(_.getName.contains("copy"))
warning: there was one deprecation warning; re-run with -deprecation for details
res5: Array[java.lang.reflect.Method] = Array(public Test Test.copy(java.lang.String), public java.lang.String Test.copy$default$1())

因此您需要手动提供字段名称。

之前我还不知道

答案 2 :(得分:0)

我建议您使用name: List[String]定义案例类,并使用自定义应用函数创建伴随对象。您仍然可以创建Colors("blue", "green")等对象。

case class Colors(name: List[String])

object Colors {
  def apply(name: String*): Colors = new Colors(name.toList)
  def apply(names: List[String]): Colors = new Colors(names)
}


Colors("blue", "green")