【问题标题】:Better way to deserialize Minecraft json反序列化 Minecraft json 的更好方法
【发布时间】:2015-06-27 01:07:00
【问题描述】:

我正在尝试使用来自 (1.8.json download) 的 Minecraft json。一个样本:

{
  "objects": {
    "realms/lang/de_DE.lang": {
      "hash": "729b2c09d5c588787b23127eeda2730f9c039194",
      "size": 7784
    },
    "realms/lang/cy_GB.lang": {
      "hash": "7b52463b2df4685d2d82c5d257fd5ec79843d618",
      "size": 7688
    },
    "minecraft/sounds/mob/blaze/breathe4.ogg": {
      "hash": "78d544a240d627005aaef6033fd646eafc66fe7a",
      "size": 22054
    },
    "minecraft/sounds/dig/sand4.ogg": {
      "hash": "37afa06f97d58767a1cd1382386db878be1532dd",
      "size": 5491
    }
  }
}

实际的 json 要长得多,大约 2940 行。

我需要一种不完全疯狂的反序列化方法 - 使用 JSONUtils 我得到 4411 行代码,但相同的代码不能用于任何其他版本的 Minecraft。

【问题讨论】:

    标签: json vb.net visual-studio-2013 deserialization


    【解决方案1】:

    自动化工具可能非常有用,但它们并不完美——尤其是在字典方面。

    首先要注意的是,它们的结构都是相同的,即它们都由hashsize 属性组成。这意味着我们可以一遍又一遍地使用同一个类,而不是创建数百个相同的类:

    ' the type that repeats from your robot:
    Public Class MinecraftItem
        Public Property hash As String
        Public Property size As Int32
    End Class
    

    由于自动化工具在运行中(它们不会向前看……或向后看),它们并不真正知道它们都是一样的。接下来是机器人生成的类在这种情况下不起作用:

    Public Property minecraft/sounds/music/game/creative/creative3.ogg As _
                MinecraftSoundsMusicGameCreativeCreative3Ogg
    

    作为属性名称是非法的。但是,这些名称可以作为 Dictionary 键正常工作。除了上面的MinecraftItem 类之外,我们可能想要一个容器类:

    Public Class MinecraftContainer
        Public objects As Dictionary(Of String, MinecraftItem)
    End Class
    

    (至少)有 3 种获取数据的方法:

    方法一:单项取出

    Imports Newtonsoft.Json
    Imports Newtonsoft.Json.Linq
    
    Dim jstr As String = ...from whereever
    
    ' parse the json into a JObject
    Dim js As JObject = JObject.Parse(jstr)
    
    ' if you somehow know the names, you can pluck out the data:
    Dim mi = js("objects")("minecraft/sounds/mob/blaze/hit2.ogg")
    
    Console.WriteLine(mi("hash").ToString & "    " & mi("size").ToString)
    

    第一行将原始 json 字符串解析为 JObject。这使我们能够以各种方式处理内容。例如,我们可以在 json 中引用“对象”,并通过名称引用它们,这正是下一行发生的事情:

    ' drill into "objects", get the "...hit2.ogg" item
    Dim mi = js("objects")("minecraft/sounds/mob/blaze/hit2.ogg")
    

    如果您只需要从大文件中获取特定项目,这将起作用。创建的mi 变量是一个“特殊”的 json 标记,因此使用名称来获取您想要的数据位

    hash = mi("hash").ToString
    size = mi("size").ToString
    

    方法二:反序列化为字典

    这将是我的首选方法。包含与第一个示例相同的 Import 语句,然后:

    ' parse the json string
    Dim js As JObject = JObject.Parse(jstr)
    
    ' deserialize the inner "objects" to a NET Dictionary
    Dim myItems = JsonConvert.DeserializeObject(Of Dictionary(Of String, _
                       MinecraftItem))(js("objects").ToString)
    

    这将从 json 创建 myItems 作为 Net Dictionary(Of String, MincraftItem)。由于MinecraftObject 类不做任何事情,而是保存字典,因此我们跳过了它。

    键是长名称,每个值都是MinecraftItem,这使您可以更常规地引用它们:

    ' get one of the items into a variable
    gravel3 = myItems("minecraft/sounds/mob/chicken/step2.ogg")
    ConsoleWriteLine("Gravel3  hash: {0},  size: {1}",
                          gravel3.hash, gravel3.size.ToString)
    

    如果您不熟悉.Net Dictionary,它有点像数组或列表,只是您通过Key 而不是索引访问您的项目。循环遍历它们:

    Dim n As Integer = 0
    For Each kvp As KeyValuePair(Of String, MinecraftItem) In myItems
        Console.WriteLine("Name: {0}  Hash: {1}  size: {2}",
                          kvp.Key, 
                          kvp.Value.hash, 
                          kvp.Value.size.ToString)
        n += 1
        If n >= 2 Then Exit For           ' just print 3
    Next                              
    

    输出:

    名称:realms/lang/de_DE.lang 哈希:10a54fc66c8f479bb65c8d39c3b62265ac82e742 大小:8112
    名称:realms/lang/cy_GB.lang 哈希:14cfb2f24e7d91dbc22a2a0e3b880d9829320243 大小:7347
    名称:minecraft/sounds/mob/chicken/step2.ogg 哈希:bf7fadaf64945f6b31c803d086ac6a652aabef9b 大小:3838

    请记住,Key 始终是长路径名,每个.Value 都是 MinecraftItem 对象。

    方法三:反序列化到容器

    您可以使用MinecraftContainer 类跳过解析步骤:

    Dim jstr As String = ...from whereever
    Dim myJItems = JsonConvert.DeserializeObject(Of MinecraftContainer)(jstr)
    

    请注意,这一次,您将您下载的整个字符串传递给JsonConvert。结果将是一个额外的外部对象,其中包含一个名为“对象”的字典属性。因此,您使用一些主要参考来引用这些项目:

    gravel3hash = myJItems.Object("minecraft/sounds/dig/gravel3.ogg").hash
    
    gravel3 = myJItems.Object("minecraft/sounds/dig/gravel3.ogg")
        ConsoleWriteLine("Gravel3  hash: {0},  size: {1}",
                          gravel3.hash, gravel3.size.ToString)
    'or:
    ConsoleWriteLine("Gravel3  hash: {0},  size: {1}",
                       myJItems.Object("minecraft/sounds/dig/gravel3.ogg").hash, 
                       myJItems.Object("minecraft/sounds/dig/gravel3.ogg").size.ToString)   
    

    这个方法只是反序列化的一行代码,但是意思是
    a) 我必须定义容器类和
    b) 我必须使用myJItems.Object 钻入原本为空的容器以获取字典。

    就我个人而言,我会放弃这个并使用方法 2 - 多行代码使它更容易使用。也就是说,您还可以从容器中提取字典集合:

    Dim myItems As Dictionary(Of String, MinecraftItem)= myJItems.Object
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多