2012-12-18 3 views
8

저는 스칼라로 작성된 작은 GUI 응용 프로그램을 만들고 있습니다. 사용자가 GUI에서 설정하는 몇 가지 설정이 있으며이를 프로그램 실행간에 유지하려고합니다. 기본적으로 나는 scala.collections.mutable.Map을 원한다. 수정 될 때 파일에 자동으로 지속된다.Scala 응용 프로그램의 기본 구성은 어떻게 제공합니까?

일반적인 문제 일 것 같지만 경량 솔루션을 찾지 못했습니다. 이 문제는 일반적으로 어떻게 해결됩니까?

+0

나는 json4s https://github.com/json4s/json4s를 사용하여 JSON에 직렬화를 종료했다. 모든 제안을 주셔서 감사합니다. – Dave

답변

9

나는 이것을 많이하고 .properties 파일을 사용한다. (Java-land에서는 관용적이다.) 그래도 내 설정을 꽤 간단하게 유지합니다. 중첩 된 설정 구성을 가지고 있다면 YAML (사람이 주 저자 인 경우)이나 JSON 또는 XML (기계가 작성자 인 경우)과 같은 다른 형식이 필요할 수 있습니다.

import java.io._ 
import java.util._ 
import scala.collection.JavaConverters._ 

val f = new File("test.properties") 
// test.properties: 
// foo=bar 
// baz=123 

val props = new Properties 

// Note: in real code make sure all these streams are 
// closed carefully in try/finally 
val fis = new InputStreamReader(new FileInputStream(f), "UTF-8") 
props.load(fis) 
fis.close() 

println(props) // {baz=123, foo=bar} 

val map = props.asScala // Get to Scala Map via JavaConverters 
map("foo") = "42" 
map("quux") = "newvalue" 

println(map) // Map(baz -> 123, quux -> newvalue, foo -> 42) 
println(props) // {baz=123, quux=newvalue, foo=42} 

val fos = new OutputStreamWriter(new FileOutputStream(f), "UTF-8") 
props.store(fos, "") 
fos.close() 
+0

asScala는 중첩 형식에 사용할 수 있습니까?JSON 객체와 같은 것을로드한다고 가정 해 봅시다. 일부 값이 다른지도 또는 목록이고 다른지도, 목록, 숫자, 문자열 등을 포함하는지도를 다시 가져오고 싶을 수도 있습니다. – user48956

0

지도를 속성으로 변환하거나 그 반대의 경우를 권장합니다. "* .properties"파일은 Java 환경에서 구성을 저장하기위한 표준입니다. 스칼라에 사용하지 않는 이유는 무엇입니까?

0

일반적인 방법은 *입니다. 속성, * .xml, scala는 기본적으로 xml을 지원하므로 java에서 xml config를 사용하면 더 쉽습니다.

1

가 여기에 설정을 읽기위한 XML 및 케이스 클래스를 사용하는 예이다 :

는 여기서 다시 .properties 파일로 저장 한 후, 스칼라지도로 조작,로드 소품에 대한 몇 가지 예제 코드입니다. 실제 수업은지도보다 좋을 수 있습니다. (당신은 또한 sbt와 적어도 하나의 프로젝트가 스칼라 소스로 설정을 가져 와서 컴파일 할 수 있습니다; 저장은 자동으로 수행하지 않습니다. 또는 repl 스크립트로 수행합니다. 나는 봤지만 굳이 찾아 내지 못했을 것입니다.

case class PluginDescription(name: String, classname: String) { 
    def toXML: Node = { 
    <plugin> 
     <name>{name}</name> 
     <classname>{classname}</classname> 
    </plugin> 
    } 
} 
object PluginDescription { 

    def fromXML(xml: Node): PluginDescription = { 
    // extract one field 
    def getField(field: String): Option[String] = { 
     val text = (xml \\ field).text.trim 
     if (text == "") None else Some(text) 
    } 
    def extracted = { 
     val name = "name" 
     val claas = "classname" 
     val vs = Map(name -> getField(name), claas -> getField(claas)) 
     if (vs.values exists (_.isEmpty)) fail() 
     else PluginDescription(name = vs(name).get, classname = vs(claas).get) 
    } 
    def fail() = throw new RuntimeException("Bad plugin descriptor.") 
    // check the top-level tag 
    xml match { 
     case <plugin>{_*}</plugin> => extracted 
     case _      => fail() 
    } 
    } 
} 

이 코드는 반사적이 경우 클래스의 적용 호출)

Here's the simpler code.

이 버전의 경우 클래스를 사용합니다. 유스 케이스는 config에서 누락 된 필드가 기본 args에 의해 제공 될 수 있다는 것이다. 여기서 유형 변환이 없습니다. 예 : case class Config(foo: String = "bar").

// isn't it easier to write a quick loop to reflect the field names? 
import scala.reflect.runtime.{currentMirror => cm, universe => ru} 
import ru._ 

def fromXML(xml: Node): Option[PluginDescription] = { 
    def extract[A]()(implicit tt: TypeTag[A]): Option[A] = { 
    // extract one field 
    def getField(field: String): Option[String] = { 
     val text = (xml \\ field).text.trim 
     if (text == "") None else Some(text) 
    } 

    val apply = ru.newTermName("apply") 
    val module = ru.typeOf[A].typeSymbol.companionSymbol.asModule 
    val ts = module.moduleClass.typeSignature 
    val m = (ts member apply).asMethod 
    val im = cm reflect (cm reflectModule module).instance 
    val mm = im reflectMethod m 

    def getDefault(i: Int): Option[Any] = { 
     val n = ru.newTermName("apply$default$" + (i+1)) 
     val m = ts member n 
     if (m == NoSymbol) None 
     else Some((im reflectMethod m.asMethod)()) 
    } 
    def extractArgs(pss: List[List[Symbol]]): List[Option[Any]] = 
     pss.flatten.zipWithIndex map (p => getField(p._1.name.encoded) orElse getDefault(p._2)) 
    val args = extractArgs(m.paramss) 
    if (args exists (!_.isDefined)) None 
    else Some(mm(args.flatten: _*).asInstanceOf[A]) 
    } 
    // check the top-level tag 
    xml match { 
    case <plugin>{_*}</plugin> => extract[PluginDescription]() 
    case _      => None 
    } 
} 

XML은 loadFilesave가이 Properties에 대한 한 줄 없을 것 같다 너무 나쁘다. 모든 파일에지도/객체 직렬화로 귀결로서

$ scala 
Welcome to Scala version 2.10.0-RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_06). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> import reflect.io._ 
import reflect.io._ 

scala> import java.util._ 
import java.util._ 

scala> import java.io.{StringReader, File=>JFile} 
import java.io.{StringReader, File=>JFile} 

scala> import scala.collection.JavaConverters._ 
import scala.collection.JavaConverters._ 

scala> val p = new Properties 
p: java.util.Properties = {} 

scala> p load new StringReader(
    | (new File(new JFile("t.properties"))).slurp) 

scala> p.asScala 
res2: scala.collection.mutable.Map[String,String] = Map(foo -> bar) 
1

, 당신의 선택은 다음과 같습니다

  • 고전 직렬화가 XML에
  • 직렬화를 바이트 코드
  • JSON으로 직렬화 (쉬운 Jackson 또는 Lift-JSON 사용)
  • 속성 파일 사용 (추한, 아니 utf-8 지원)
  • propr에 직렬화 (추악한, 왜 바퀴를 재발견)
관련 문제