summaryrefslogblamecommitdiff
path: root/src/main/scala/cc/spray/json/ProductFormats.scala
blob: 3bd3783ffeb4bc9c2fe98571a2dac10e4e0ff60c (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  
                                     













                                                                           
                     

   

                                                                                                                   
   
                      
                          
 




                                                                                                               
                               
                                      

                                         
                            


     




                                                                                                                                      
                               

                                       

                                         

                             


     



                                                                                                                     
                                                                              
                                                                                    
                               


                                        

                                         


                             


     




                                                                          
                                                                                        
                                                                                               
                               



                                         

                                         



                             


     




                                                                                 
                                                                                                  
                                                                                                          
                               




                                          

                                         




                             

     





                                                                                        
                                                                                                            
                                                                                                                     
                               





                                           

                                         





                             

     





                                                                                               
                                                                                                                      
                                                                                                                                
                               






                                            

                                         






                             

     





                                                                                                      
                                                                                      
                                                  
                                                                                                                                            
                               







                                             

                                         







                             

     





                                                                                                             
                                                                                             
                                                                                                                       
                                                                                     
                               








                                              

                                         








                             

     





                                                                                                                     
                                                                                                    
                                                                                                               
                                                                                                           
                               









                                               

                                         









                             

     





                                                                                                                            
                                                                                                           
                                                                                                                  
                                                                                                                      
                               










                                                 

                                         










                             


     




                                                                                                                                   

                                                                                                                     
                                                                                                                                 
                               











                                                  

                                         











                             


     




                                                                                                                                          

                                                                                                                         
                                                                                                                                            
                               












                                                   

                                         












                             


     




                                                                                                                                                 
                                                                                                                                

                                                                                                                
                                                               
                               













                                                    

                                         













                             


     




                                                                                                                                                        
                                                                                                                                       

                                                                                                                      
                                                               
                               














                                                     

                                         














                             


     

            

                                                                                                          


                                                        
                                                        

     
 
                                                                                                 
                 









                                                                          
                                                                                                 
         
                                                       
     
   



                                                                                     



                                                                                                                  

                                                                               
                                                                                
                                                                                                
                                                                                

                           

                                                                                                                       

     
 
 






                                                                                                                      





                                                                                                             
                                            
   
 
/*
 * Copyright (C) 2011 Mathias Doenitz
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cc.spray.json

/**
 * Provides the helpers for constructing custom JsonFormat implementations for types implementing the Product trait
 * (especially case classes)
 */
trait ProductFormats {
  this: StandardFormats =>

  def jsonFormat1[A :JF, T <: Product :ClassManifest](construct: A => T): RootJsonFormat[T] = {
    val Array(a) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a)
  }
  def jsonFormat[A :JF, T <: Product](construct: A => T, a: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p, 0)
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a)
    )
  }

  def jsonFormat2[A :JF, B :JF, T <: Product :ClassManifest](construct: (A, B) => T): RootJsonFormat[T] = {
    val Array(a, b) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b)
  }
  def jsonFormat[A :JF, B :JF, T <: Product](construct: (A, B) => T, a: String, b: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p, 0,
      productElement2Field[B](b, p, 1))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b)
    )
  }

  def jsonFormat3[A :JF, B :JF, C :JF, T <: Product :ClassManifest](construct: (A, B, C) => T): RootJsonFormat[T] = {
    val Array(a, b, c) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c)
  }
  def jsonFormat[A :JF, B :JF, C :JF, T <: Product](construct: (A, B, C) => T,
        a: String, b: String, c: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p, 0,
      productElement2Field[B](b, p, 1,
      productElement2Field[C](c, p, 2)))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c)
    )
  }

  def jsonFormat4[A :JF, B :JF, C :JF, D :JF, T <: Product :ClassManifest]
    (construct: (A, B, C, D) => T): RootJsonFormat[T] = {
    val Array(a, b, c, d) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c, d)
  }
  def jsonFormat[A :JF, B :JF, C :JF, D :JF, T <: Product](construct: (A, B, C, D) => T,
        a: String, b: String, c: String, d: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p, 0,
      productElement2Field[B](b, p, 1,
      productElement2Field[C](c, p, 2,
      productElement2Field[D](d, p, 3))))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c),
      fromField[D](value, d)
    )
  }

  def jsonFormat5[A :JF, B :JF, C :JF, D :JF, E :JF, T <: Product :ClassManifest]
    (construct: (A, B, C, D, E) => T): RootJsonFormat[T] = {
    val Array(a, b, c, d, e) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c, d, e)
  }
  def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, T <: Product](construct: (A, B, C, D, E) => T,
        a: String, b: String, c: String, d: String, e: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p, 0,
      productElement2Field[B](b, p, 1,
      productElement2Field[C](c, p, 2,
      productElement2Field[D](d, p, 3,
      productElement2Field[E](e, p, 4)))))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c),
      fromField[D](value, d),
      fromField[E](value, e)
    )
  }

  def jsonFormat6[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, T <: Product :ClassManifest]
    (construct: (A, B, C, D, E, F) => T): RootJsonFormat[T] = {
    val Array(a, b, c, d, e, f) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c, d, e, f)
  }
  def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, T <: Product](construct: (A, B, C, D, E, F) => T,
        a: String, b: String, c: String, d: String, e: String, f: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p, 0,
      productElement2Field[B](b, p, 1,
      productElement2Field[C](c, p, 2,
      productElement2Field[D](d, p, 3,
      productElement2Field[E](e, p, 4,
      productElement2Field[F](f, p, 5))))))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c),
      fromField[D](value, d),
      fromField[E](value, e),
      fromField[F](value, f)
    )
  }

  def jsonFormat7[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, T <: Product :ClassManifest]
    (construct: (A, B, C, D, E, F, G) => T): RootJsonFormat[T] = {
    val Array(a, b, c, d, e, f, g) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c, d, e, f, g)
  }
  def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, T <: Product](construct: (A, B, C, D, E, F, G) => T,
        a: String, b: String, c: String, d: String, e: String, f: String, g: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p, 0,
      productElement2Field[B](b, p, 1,
      productElement2Field[C](c, p, 2,
      productElement2Field[D](d, p, 3,
      productElement2Field[E](e, p, 4,
      productElement2Field[F](f, p, 5,
      productElement2Field[G](g, p, 6)))))))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c),
      fromField[D](value, d),
      fromField[E](value, e),
      fromField[F](value, f),
      fromField[G](value, g)
    )
  }

  def jsonFormat8[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, T <: Product :ClassManifest]
    (construct: (A, B, C, D, E, F, G, H) => T): RootJsonFormat[T] = {
    val Array(a, b, c, d, e, f, g, h) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c, d, e, f, g, h)
  }
  def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, T <: Product]
        (construct: (A, B, C, D, E, F, G, H) => T,
         a: String, b: String, c: String, d: String, e: String, f: String, g: String, h: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p, 0,
      productElement2Field[B](b, p, 1,
      productElement2Field[C](c, p, 2,
      productElement2Field[D](d, p, 3,
      productElement2Field[E](e, p, 4,
      productElement2Field[F](f, p, 5,
      productElement2Field[G](g, p, 6,
      productElement2Field[H](h, p, 7))))))))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c),
      fromField[D](value, d),
      fromField[E](value, e),
      fromField[F](value, f),
      fromField[G](value, g),
      fromField[H](value, h)
    )
  }

  def jsonFormat9[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, T <: Product :ClassManifest]
    (construct: (A, B, C, D, E, F, G, H, I) => T): RootJsonFormat[T] = {
    val Array(a, b, c, d, e, f, g, h, i) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c, d, e, f, g, h, i)
  }
  def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, T <: Product]
        (construct: (A, B, C, D, E, F, G, H, I) => T, a: String, b: String, c: String, d: String, e: String, f: String,
         g: String, h: String, i: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p, 0,
      productElement2Field[B](b, p, 1,
      productElement2Field[C](c, p, 2,
      productElement2Field[D](d, p, 3,
      productElement2Field[E](e, p, 4,
      productElement2Field[F](f, p, 5,
      productElement2Field[G](g, p, 6,
      productElement2Field[H](h, p, 7,
      productElement2Field[I](i, p, 8)))))))))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c),
      fromField[D](value, d),
      fromField[E](value, e),
      fromField[F](value, f),
      fromField[G](value, g),
      fromField[H](value, h),
      fromField[I](value, i)
    )
  }

  def jsonFormat10[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, T <: Product :ClassManifest]
    (construct: (A, B, C, D, E, F, G, H, I, J) => T): RootJsonFormat[T] = {
    val Array(a, b, c, d, e, f, g, h, i, j) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c, d, e, f, g, h, i, j)
  }
  def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, T <: Product]
        (construct: (A, B, C, D, E, F, G, H, I, J) => T, a: String, b: String, c: String, d: String, e: String,
         f: String, g: String, h: String, i: String, j: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p, 0,
      productElement2Field[B](b, p, 1,
      productElement2Field[C](c, p, 2,
      productElement2Field[D](d, p, 3,
      productElement2Field[E](e, p, 4,
      productElement2Field[F](f, p, 5,
      productElement2Field[G](g, p, 6,
      productElement2Field[H](h, p, 7,
      productElement2Field[I](i, p, 8,
      productElement2Field[J](j, p, 9))))))))))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c),
      fromField[D](value, d),
      fromField[E](value, e),
      fromField[F](value, f),
      fromField[G](value, g),
      fromField[H](value, h),
      fromField[I](value, i),
      fromField[J](value, j)
    )
  }

  def jsonFormat11[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, T <: Product :ClassManifest]
    (construct: (A, B, C, D, E, F, G, H, I, J, K) => T): RootJsonFormat[T] = {
    val Array(a, b, c, d, e, f, g, h, i, j, k) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k)
  }
  def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, T <: Product]
        (construct: (A, B, C, D, E, F, G, H, I, J, K) => T, a: String, b: String, c: String, d: String, e: String,
         f: String, g: String, h: String, i: String, j: String, k: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p, 0,
      productElement2Field[B](b, p, 1,
      productElement2Field[C](c, p, 2,
      productElement2Field[D](d, p, 3,
      productElement2Field[E](e, p, 4,
      productElement2Field[F](f, p, 5,
      productElement2Field[G](g, p, 6,
      productElement2Field[H](h, p, 7,
      productElement2Field[I](i, p, 8,
      productElement2Field[J](j, p, 9,
      productElement2Field[K](k, p, 10)))))))))))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c),
      fromField[D](value, d),
      fromField[E](value, e),
      fromField[F](value, f),
      fromField[G](value, g),
      fromField[H](value, h),
      fromField[I](value, i),
      fromField[J](value, j),
      fromField[K](value, k)
    )
  }

  def jsonFormat12[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L: JF, T <: Product :ClassManifest]
    (construct: (A, B, C, D, E, F, G, H, I, J, K, L) => T): RootJsonFormat[T] = {
    val Array(a, b, c, d, e, f, g, h, i, j, k, l) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l)
  }
  def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, T <: Product]
        (construct: (A, B, C, D, E, F, G, H, I, J, K, L) => T, a: String, b: String, c: String, d: String, e: String,
         f: String, g: String, h: String, i: String, j: String, k: String, l: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p,  0,
      productElement2Field[B](b, p,  1,
      productElement2Field[C](c, p,  2,
      productElement2Field[D](d, p,  3,
      productElement2Field[E](e, p,  4,
      productElement2Field[F](f, p,  5,
      productElement2Field[G](g, p,  6,
      productElement2Field[H](h, p,  7,
      productElement2Field[I](i, p,  8,
      productElement2Field[J](j, p,  9,
      productElement2Field[K](k, p, 10,
      productElement2Field[L](l, p, 11))))))))))))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c),
      fromField[D](value, d),
      fromField[E](value, e),
      fromField[F](value, f),
      fromField[G](value, g),
      fromField[H](value, h),
      fromField[I](value, i),
      fromField[J](value, j),
      fromField[K](value, k),
      fromField[L](value, l)
    )
  }

  def jsonFormat13[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L: JF, M :JF, T <: Product :ClassManifest]
    (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M) => T): RootJsonFormat[T] = {
    val Array(a, b, c, d, e, f, g, h, i, j, k, l, m) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m)
  }
  def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, M :JF, T <: Product]
        (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M) => T, a: String, b: String, c: String, d: String, e: String,
         f: String, g: String, h: String, i: String, j: String, k: String, l: String, m: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p,  0,
      productElement2Field[B](b, p,  1,
      productElement2Field[C](c, p,  2,
      productElement2Field[D](d, p,  3,
      productElement2Field[E](e, p,  4,
      productElement2Field[F](f, p,  5,
      productElement2Field[G](g, p,  6,
      productElement2Field[H](h, p,  7,
      productElement2Field[I](i, p,  8,
      productElement2Field[J](j, p,  9,
      productElement2Field[K](k, p, 10,
      productElement2Field[L](l, p, 11,
      productElement2Field[M](m, p, 12)))))))))))))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c),
      fromField[D](value, d),
      fromField[E](value, e),
      fromField[F](value, f),
      fromField[G](value, g),
      fromField[H](value, h),
      fromField[I](value, i),
      fromField[J](value, j),
      fromField[K](value, k),
      fromField[L](value, l),
      fromField[M](value, m)
    )
  }

  def jsonFormat14[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L: JF, M :JF, N :JF, T <: Product :ClassManifest]
    (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N) => T): RootJsonFormat[T] = {
    val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n)
  }
  def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, M :JF, N :JF, T <: Product]
        (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N) => T, a: String, b: String, c: String, d: String,
         e: String, f: String, g: String, h: String, i: String, j: String, k: String, l: String, m: String,
         n: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p,  0,
      productElement2Field[B](b, p,  1,
      productElement2Field[C](c, p,  2,
      productElement2Field[D](d, p,  3,
      productElement2Field[E](e, p,  4,
      productElement2Field[F](f, p,  5,
      productElement2Field[G](g, p,  6,
      productElement2Field[H](h, p,  7,
      productElement2Field[I](i, p,  8,
      productElement2Field[J](j, p,  9,
      productElement2Field[K](k, p, 10,
      productElement2Field[L](l, p, 11,
      productElement2Field[M](m, p, 12,
      productElement2Field[N](n, p, 13))))))))))))))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c),
      fromField[D](value, d),
      fromField[E](value, e),
      fromField[F](value, f),
      fromField[G](value, g),
      fromField[H](value, h),
      fromField[I](value, i),
      fromField[J](value, j),
      fromField[K](value, k),
      fromField[L](value, l),
      fromField[M](value, m),
      fromField[N](value, n)
    )
  }

  def jsonFormat15[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L: JF, M :JF, N :JF, O :JF, T <: Product :ClassManifest]
    (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) => T): RootJsonFormat[T] = {
    val Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) = extractFieldNames(classManifest[T])
    jsonFormat(construct, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)
  }
  def jsonFormat[A :JF, B :JF, C :JF, D :JF, E :JF, F :JF, G :JF, H :JF, I :JF, J :JF, K :JF, L :JF, M :JF, N :JF, O :JF, T <: Product]
        (construct: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) => T, a: String, b: String, c: String, d: String,
         e: String, f: String, g: String, h: String, i: String, j: String, k: String, l: String, m: String, n: String,
         o: String): RootJsonFormat[T] = new RootJsonFormat[T]{
    def write(p: T) = JsObject(
      productElement2Field[A](a, p,  0,
      productElement2Field[B](b, p,  1,
      productElement2Field[C](c, p,  2,
      productElement2Field[D](d, p,  3,
      productElement2Field[E](e, p,  4,
      productElement2Field[F](f, p,  5,
      productElement2Field[G](g, p,  6,
      productElement2Field[H](h, p,  7,
      productElement2Field[I](i, p,  8,
      productElement2Field[J](j, p,  9,
      productElement2Field[K](k, p, 10,
      productElement2Field[L](l, p, 11,
      productElement2Field[M](m, p, 12,
      productElement2Field[N](n, p, 13,
      productElement2Field[O](o, p, 14)))))))))))))))
    )
    def read(value: JsValue) = construct(
      fromField[A](value, a),
      fromField[B](value, b),
      fromField[C](value, c),
      fromField[D](value, d),
      fromField[E](value, e),
      fromField[F](value, f),
      fromField[G](value, g),
      fromField[H](value, h),
      fromField[I](value, i),
      fromField[J](value, j),
      fromField[K](value, k),
      fromField[L](value, l),
      fromField[M](value, m),
      fromField[N](value, n),
      fromField[O](value, o)
    )
  }

  // helpers
  
  protected def productElement2Field[T](fieldName: String, p: Product, ix: Int, rest: List[JsField] = Nil)
                                       (implicit writer: JsonWriter[T]): List[JsField] = {
    val value = p.productElement(ix).asInstanceOf[T]
    writer match {
      case _: OptionFormat[_] if (value == None) => rest
      case _ => (fieldName, writer.write(value)) :: rest
    }
  }

  private def fromField[T](value: JsValue, fieldName: String)(implicit reader: JsonReader[T]) = {
    value match {
      case x: JsObject =>
        var fieldFound = false
        try {
          val fieldValue = x.fields(fieldName)
          fieldFound = true
          reader.read(fieldValue)
        }
        catch {
          case e: NoSuchElementException if !fieldFound =>
            if (reader.isInstanceOf[OptionFormat[_]]) None.asInstanceOf[T]
            else deserializationError("Object is missing required member '" + fieldName + "'", e)
        }
      case _ => deserializationError("Object expected")
    }
  }

  protected def extractFieldNames(classManifest: ClassManifest[_]): Array[String] = {
    val clazz = classManifest.erasure
    try {
      // copy methods have the form copy$default$N(), we need to sort them in order, but must account for the fact
      // that lexical sorting of ...8(), ...9(), ...10() is not correct, so we extract N and sort by N.toInt
      val copyDefaultMethods = clazz.getMethods.filter(_.getName.startsWith("copy$default$")).sortBy(
        _.getName.drop("copy$default$".length).takeWhile(_ != '(').toInt)
      val fields = clazz.getDeclaredFields.filterNot(_.getName.startsWith("$"))
      if (copyDefaultMethods.length != fields.length)
        sys.error("Case class " + clazz.getName + " declares additional fields")
      if (fields.zip(copyDefaultMethods).exists { case (f, m) => f.getType != m.getReturnType })
        sys.error("Cannot determine field order of case class " + clazz.getName)
      fields.map(_.getName)
    } catch {
      case ex => throw new RuntimeException("Cannot automatically determine case class field names and order " +
        "for '" + clazz.getName + "', please use the 'jsonFormat' overload with explicit field name specification", ex)
    }
  }
}

/**
 * This trait supplies an alternative rendering mode for optional case class members.
 * Normally optional members that are undefined (`None`) are not rendered at all.
 * By mixing in this trait into your custom JsonProtocol you can enforce the rendering of undefined members as `null`.
 * (Note that this only affect JSON writing, spray-json will always read missing optional members as well as `null`
 * optional members as `None`.)
 */
trait NullOptions extends ProductFormats {
  this: StandardFormats =>

  override protected def productElement2Field[T](fieldName: String, p: Product, ix: Int, rest: List[JsField])
                                                (implicit writer: JsonWriter[T]) = {
    val value = p.productElement(ix).asInstanceOf[T]
    (fieldName, writer.write(value)) :: rest
  }
}