You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala解析YAML文件过滤零值子类别时类型转换报错求助

Fixing the BigInt cannot be cast to Double Error When Filtering circe JSON Values

Hey there! Let's break down why you're hitting that cast error and fix it properly with idiomatic Scala + circe code.

The Root Cause

When circe parses your YAML, it infers numeric types automatically:

  • Integer values like 0 get parsed as BigInt
  • Decimal values like 1.5 get parsed as BigDecimal

Your original code tries to force-cast everything to Double with asInstanceOf[Map[String,Double]], which fails because BigInt can't be directly cast to Double. Instead, we need to handle numeric types safely using circe's built-in APIs.

Step-by-Step Solution

First, let's clean up the parsing flow (no need to convert JSON to a string and re-parse it—we can work directly with the parsed Json value):

import io.circe._
import io.circe.yaml.parser

// Your YAML content
val ymlText = """
ALL:
  Category1:
    Subcategory11 : 1.5
    Subcategory12 : 0
    Subcategory13 : 0
    Subcategory14 : 0.5
  Category2:
    Subcategory21 : 1.5
    Subcategory22 : 0.3
    Subcategory23 : 0
    Subcategory24 : 0
"""

// Parse YAML to Json (use valueOr to handle parsing errors cleanly, avoid null!)
val json: Json = parser.parse(ymlText).valueOr(throw _)

Now, let's safely filter out subcategories with a value of 0. We'll use circe's type-safe extraction methods instead of risky casts:

// Extract the "ALL" object from the JSON
val allCategories = json.hcursor.downField("ALL").focus.getOrElse(Json.obj())

// Filter out subcategories with value 0 (handle all numeric types safely)
val filteredCategories = allCategories.as[Map[String, Map[String, Json]]].valueOr(throw _).map {
  case (categoryName, subcategories) =>
    val filteredSubcats = subcategories.filter {
      case (subcatName, value) =>
        // Convert the Json value to Double safely, then check if it's > 0
        value.as[Double].fold(
          _ => false, // Ignore non-numeric values if any
          num => num > 0.0
        )
    }
    (categoryName, filteredSubcats)
}

// Print the result
println(filteredCategories)

Alternative: Direct Numeric Type Checking

If you prefer to work with circe's Number type directly (to avoid converting to Double unnecessarily), you can do this:

val filteredCategoriesAlt = allCategories.asObject.get.toMap.map {
  case (categoryName, subcatsJson) =>
    val filteredSubcats = subcatsJson.asObject.get.toMap.filter {
      case (subcatName, value) =>
        value.asNumber match {
          case Some(num) => 
            // Check if the number is greater than 0 (works for BigInt and BigDecimal)
            num.toDouble.exists(_ > 0.0)
          case None => false // Skip non-numeric entries
        }
    }
    (categoryName, filteredSubcats)
}

println(filteredCategoriesAlt)

Key Improvements Over Your Original Code

  1. No risky casts: We use circe's as and asNumber methods to handle type conversions safely.
  2. Avoid nulls: Using valueOr and getOrElse ensures we handle missing values gracefully instead of relying on null.
  3. Idiomatic Scala: Using val instead of var aligns with functional programming practices.

Expected Output

Both solutions will produce the filtered result:

Map(
  Category1 -> Map(Subcategory11 -> 1.5, Subcategory14 -> 0.5),
  Category2 -> Map(Subcategory21 -> 1.5, Subcategory22 -> 0.3)
)

内容的提问来源于stack exchange,提问作者BrDATA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:05:29