8

私はScalaで書かれた小さなGUIアプリケーションに取り組んでいます。ユーザーがGUIで設定する設定がいくつかあり、プログラムの実行間でそれらを保持したいと思います。基本的に、変更時にファイルに自動的に保持されるscala.collections.mutable.Mapが必要です。

これはよくある問題のようですが、軽量の解決策を見つけることができませんでした。この問題は通常どのように解決されますか?

4

5 に答える 5

9

私はこれをたくさん行い、.propertiesファイルを使用します(Javaランドでは慣用的です)。ただし、設計上、構成は非常に単純です。ネストされた構成構造がある場合は、YAML(人間が主な作成者の場合)やJSONまたはXML(マシンが作成者の場合)などの別の形式が必要になる場合があります。

小道具をロードし、Scala Mapとして操作し、.propertiesとして再度保存するためのサンプルコードを次に示します。

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()
于 2012-12-18T02:58:44.140 に答える
1

マップ/オブジェクトをファイルにシリアル化することになりますので、選択できるのは次のとおりです。

  • バイトコードへの従来のシリアル化
  • XMLへのシリアル化
  • JSONへのシリアル化(JacksonまたはLift-JSONを使用して簡単に)
  • プロパティファイルの使用(醜い、utf-8サポートなし)
  • プロプライエタリフォーマットへのシリアル化(醜い、なぜ車輪の再発明をするのか)
于 2012-12-18T11:35:51.247 に答える
1

これは、構成を読み取るためにXMLとケースクラスを使用する例です。実際のクラスは、マップよりも優れている場合があります。(sbtと少なくとも1つのプロジェクトが行うことを実行し、構成をScalaソースとして取得し、コンパイルすることもできます。保存は自動化されていません。または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()
    }
  }
}

このコードは、ケースクラスの適用を反射的に呼び出します。ユースケースは、configにないフィールドをデフォルトの引数で指定できることです。ここでは型変換はありません。例: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にはloadFile、そしてsave、それは残念ですが、のワンライナーがないようです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)
于 2012-12-18T06:43:12.187 に答える
0

マップをプロパティに、またはその逆に変換することをお勧めします。「*.properties」ファイルはJavaの世界で構成を保存するための標準ですが、Scalaで使用してみませんか?

于 2012-12-18T02:20:42.477 に答える
0

一般的な方法は*です。プロパティ、*。xml、scalaはxmlをネイティブにサポートしているため、javaよりもxmlconfigを使用する方が簡単です。

于 2012-12-18T02:38:27.337 に答える