我有一个包含25个字段的case类,需要将其转换为22个字段,其中19个是共享的,而3个只是简单地重命名。
我找到了一些使用shapeless
进行操作的示例(例如an answer here,以及Miles Sabin here和here的一些代码示例),但最后一个是这些看起来有些过时了,我无法从Github示例中弄清楚如何使用shapeless重命名多个字段,或者如何在将字段添加到新对象之前对字段进行更多操作。有人可以帮我吗?
简化的代码示例;
import shapeless.LabelledGeneric
case class A(fieldA:Int, fieldB:String, fieldC:String)
case class B(fieldARenamed:Int, fieldB:String, fieldC:String, fieldCTransformed:String)
val aGen = LabelledGeneric[A]
val bGen = LabelledGeneric[B]
val freddie = new A(1,"Freddie","somestring")
val record = aGen.to(freddie)
val atmp = freddie.fieldA
record.Remove("fielda")
val freddieB = bGen.from(record +
(Symbol("fieldARenamed") ->> atmp) +
(Symbol("fieldCTransformed") ->> freddie.fieldC.toUpperCase)
) //Errors everywhere, even if I replace + with :: etc.
我有一种Align
的感觉会出现在这里的某处,但了解如何以最精简的方式做到这一点-例如而不会像上面的第三个链接那样创建Field
之类的其他特征,将会很有趣。
在The Shapeless Guide中,还使用了单引号(例如'fieldC
)表示法,但我在其中找不到很多信息,因此如果这起了一定作用,解释也将非常有帮助。对于这种Scala巫术的深度来说,这还算是新事物,因此,如果这个问题显得晦涩难懂或涵盖了太多不同的话题,就此道歉。
编辑:为免生疑问,我不是寻找答案,这些建议表明我只是通过引用第一个字段来手动创建新的案例类,例如;
val freddieB = B(fieldARenamed = freddie.fieldA, fieldB = freddie.fieldB, fieldC = freddie.fieldC, fieldCTransformed =freddie.fieldC.toUpperCase)
出于各种原因,请参阅以下评论。
答案 0 :(得分:2)
最简单的解决方案是使用旧case class
中的值构造新import os
import pysftp
import socket
from stat import S_IMODE, S_ISDIR, S_ISREG
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None
IP = "192.168.X.X"
myUsername = "user"
port = 22
myPassword = "password"
try:
with pysftp.Connection(host=IP, username=myUsername, password=myPassword, cnopts=cnopts) as sftp:
try:
r=str(socket.gethostbyaddr(IP))
print("connection successful with "+r)
def get_r_portable(sftp, remotedir, localdir, preserve_mtime=False):
for entry in sftp.listdir(remotedir):
remotepath = remotedir + "/" + entry
localpath = os.path.join(localdir, entry)
mode = sftp.stat(remotepath).st_mode
if S_ISDIR(mode):
try:
os.mkdir(localpath, mode=777)
except OSError:
pass
get_r_portable(sftp, remotepath, localpath, preserve_mtime)
elif S_ISREG(mode):
sftp.get(remotepath, localpath, preserve_mtime=preserve_mtime)
remote_path = input("enter the remote_path: ")
local_path = input("enter the local_path: ")
get_r_portable(sftp, remote_path, local_path, preserve_mtime=False)
except socket.herror:
print("Unknown host")
except:
print("connection failed")
的实例,并根据需要将函数应用于这些值。该代码将非常高效,代码的目的将非常明确,比任何其他解决方案花费的时间更少,比依赖第三方库的解决方案更健壮和可维护,并且避免了这两个类之间存在隐藏的依赖关系。
答案 1 :(得分:2)
另一个选择是使用automapper;特别是Dynamic Mappings功能。
对于您的特定示例,其外观如下:
import io.bfil.automapper._
case class A(fieldA:Int, fieldB:String, fieldC:String)
case class B(fieldARenamed:Int, fieldB:String, fieldC:String, fieldCTransformed:String)
val freddie = new A(1,"Freddie","somestring")
val freddieB = automap(freddie).dynamicallyTo[B](
fieldARenamed = freddie.fieldA,
fieldCTransformed = freddie.fieldC.toUpperCase
)
我想你可以使其功能
def atob(a: A): B = {
automap(a).dynamicallyTo[B](
fieldARenamed = a.fieldA,
fieldCTransformed = a.fieldC.toUpperCase
)
}
从效率的角度来看,此lib使用宏,因此generated code实际上和手工编写的一样好
答案 2 :(得分:0)
仅供参考,这是一种使问题代码正常工作的方法。
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{Align,Intersection}
import shapeless.syntax.singleton._
case class A(fieldA:Int, fieldB:String, fieldC:String)
case class B(fieldARenamed:Int, fieldB:String, fieldC:String, fieldCTransformed:String)
val fromGen = LabelledGeneric[A]
val toGen = LabelledGeneric[B]
val freddie = A(1, "Freddie", "somestring")
val putARename = Symbol("fieldARenamed") ->> freddie.fieldA
val putCTrans = Symbol("fieldCTransformed") ->> freddie.fieldC.toUpperCase
trait Field { type K; type V; type F = FieldType[K, V] }
object Field {
def apply[K0,V0](sample: FieldType[K0,V0]) =
new Field { type K = K0; type V = V0 }
}
val pFieldA = Field(putARename)
val pFieldCT = Field(putCTrans)
val inter = Intersection[pFieldA.F :: pFieldCT.F :: fromGen.Repr, toGen.Repr]
val align = Align[inter.Out, toGen.Repr]
toGen.from(align(inter(putARename :: putCTrans :: fromGen.to(freddie))))
//res0: B = B(1,Freddie,somestring,SOMESTRING)