如何使用 JSON Utility 读取键中带有连字符的对象?

Posted

技术标签:

【中文标题】如何使用 JSON Utility 读取键中带有连字符的对象?【英文标题】:How do you use JSONUtility to read an object with hypens in the keys? 【发布时间】:2020-11-04 12:04:09 【问题描述】:

我正在使用JSONUtility 解析来自外部源的输入。这是一个示例 json 有效负载。


    "test": "data",
    "the-problem": "uh oh"

我的问题是如何让“问题”进入一个领域。您不能在 C# 中使用连字符作为标识符 - 所以我无法为要解析的数据创建匹配的对象结构。

[Serializable]
public class Structure 
    public string test;
    public string theProblem;  // You can't use "the-problem" - see the problem?

很遗憾,Unity 文档没有讨论这个问题。

有没有办法标记字段(可能带有属性?)以覆盖用于序列化/反序列化目的的名称?最好不要依赖第三方库,因为这个统一项目的目标是 opengl,它并不总是能很好地使用它们。

【问题讨论】:

你试过 newtonsoftjson 第三方库,它允许在下面装饰 [jsonproperty(name="the-problem")] 你可以声明类的属性 我一直在努力避免使用第三方库——这个统一项目的目标是 opengl,它并不总是能很好地与它们配合使用。但如果没有人有更本地的方式来处理这个问题,我会记住这一点。 【参考方案1】:

Newtonsoft.Json-for-Unity(WebGL 兼容):

在GitHub here 上提供 jilleJr 发布的名为 Newtonsoft.Json-for-Unity 的开源版本,它是 Newtonsoft.Json 存储库的一个分支,专门为与 Unity (包括 WebGL)一起使用而维护。

可以按照以下步骤通过 Unity 包管理器 (UPM) 安装(直接取自文档 found here)

打开 /Packages/manifest.json,为 jillejr 添加范围,然后将包添加到依赖项列表中。

  "scopedRegistries": [
    
      "name": "Packages from jillejr",
      "url": "https://npm.cloudsmith.io/jillejr/newtonsoft-json-for-unity/",
      "scopes": ["jillejr"]
    
  ],
  "dependencies": 
    "jillejr.newtonsoft.json-for-unity": "12.0.201",

    // ...
  

更新包 打开包管理器 UI 窗口 > 包管理器 然后按 jillejr.newtonsoft.json-for-unity 包上的更新按钮

使用这个包我创建了以下脚本:

using UnityEngine;
using UnityEngine.UI;

public class LoadJson : MonoBehaviour

    readonly string jsonInput = "\"test\": \"data\",\"the-problem\": \"uh oh\"";//Notice the dash in 'the-problem'
    public Text sampleText; 
    void Start()
    
        LoadNewtonsoft jsonObject = LoadNewtonsoft.FromJson(jsonInput);

        sampleText.text = jsonObject.test + " " + jsonObject.theProblem;
    

using UnityEngine;
using Newtonsoft.Json;

public class LoadNewtonsoft : MonoBehaviour

    public string test;
    [JsonProperty("the-problem")]
    public string theProblem; // Notice that we don't have a dash here

    public static LoadNewtonsoft FromJson(string textJson)
    
        return JsonConvert.DeserializeObject<LoadNewtonsoft>(textJson);
    

当为 WebGL 构建时会给出以下输出:


没有外部依赖的方法:

*请注意,该解决方案的重点是使其在**不使用**的情况下**使用其他答案/cmets中建议的NewtonSoft之类的外部库(因为OP希望避免这些),并且到目前为止不是最干净和最有可能的不是最高效的解决方案。*

这个想法是通过您的 json 文本并使用正则表达式查找任何出现的“-”(连字符),并将其替换为 C# 变量名中允许的内容(在我的示例中,我将其替换为将文本解析为对象时使用 "HYPHEN")。

private static Regex hyphenSymbolPattern = new Regex("[-]");
private static string ReplaceSymbolHyphenForTextHyphen(string json) => hyphenSymbolPattern.Replace(json, "HYPHEN");

当从 json 对象转换为 json 字符串时,则相反,将单词“HYPHEN”替换为实际的连字符“-”

//You can replace "HYPHEN" with whatever you want, just remember that the variable you want to match must also have this word at the place of the "-"
private static Regex hyphenTextpattern = new Regex("(HYPHEN)");
private static string ReplaceTextHyphenForHyphenSymbol(string json) => hyphenTextpattern.Replace(json, "-");

这会导致以下输出:

警告 现在,当前的实现还有一个问题,那就是它不区分属性的名称和数据,并且替换了 所有 出现的“-”。意味着将“uh-oh”作为数据也变成“uhHYPHENoh”。

当然,这也可以通过添加一个正则表达式/函数来解决,该函数检查前一个匹配项是属性名称还是数据,但我认为这超出了这个问题的范围。

为了完整起见,这里是用于我的示例的整个代码:

//LoadJson.cs
using UnityEngine;

public class LoadJson : MonoBehaviour

    readonly string jsonInput = "\"test\": \"data\",\"the-problem\": \"uh oh\"";

    void Start()
    
        TestJson json = TestJson.FromJson(jsonInput);
        Debug.LogFormat("Parsed json object- test: 0, the-problem: 1", json.test, json.theHYPHENproblem);

        TestJson.ToJson(json);
    

//TestJson.cs
using System;
using System.Text.RegularExpressions;
using UnityEngine;

[Serializable]
public class TestJson

    public string test;
    public string theHYPHENproblem;

    private static Regex hyphenTextpattern = new Regex("(HYPHEN)"); 
    private static Regex hyphenSymbolPattern = new Regex("[-]");


    public static TestJson FromJson(string jsonText)
    
        Debug.LogFormat("Input JsonText: 0", jsonText);
        jsonText = ReplaceSymbolHyphenForTextHyphen(jsonText);
        Debug.LogFormat("Replaced jsonText: 0", jsonText);
        return JsonUtility.FromJson<TestJson>(jsonText);
    

    public static void ToJson(TestJson jsonObj)
    
        var jsonText = JsonUtility.ToJson(jsonObj);
        jsonText = ReplaceTextHyphenForHyphenSymbol(jsonText);
        Debug.LogFormat("Serialized from object Json text: 0", jsonText);
    

    private static string ReplaceTextHyphenForHyphenSymbol(string json) => hyphenTextpattern.Replace(json, "-");
    private static string ReplaceSymbolHyphenForTextHyphen(string json) => hyphenSymbolPattern.Replace(json, "HYPHEN");

正如您所看到的,这确实需要您将连字符变量命名为 string fooHYPHENBarbool isHYPHENThisHYPHENReal,诚然,在调用这些变量时,这对于可读性或整体清洁度来说并不是很好(重新考虑替换 @987654342 @ 和_而不是“连字符”可能会起作用并且不会太可怕)。但它不需要对您的 json 进行任何更改(例如删除所有出现的连字符并将其重命名为 theproblem,我相信您有理由想要包含连字符)。


使用 Newtonsoft.json(无 WebGL):

或者,如果您想尝试使用可以以更简洁的方式为您完成所有这些工作的第三方库,我建议您使用NewtonSoft json。您可以使用 NuGet 包管理器安装它(我们后来发现这适用于 WebGL,但适用于大多数其他平台)。

要将其导入 Unity:

导航到 Visual Studio 内的 Tools &gt; NuGet package manager &gt; Manage NuGet Packages for Solution。这将打开 NuGet 包管理器。 browse Newtonsoft 很可能是第一个选项,如果不是,您可以使用搜索栏进行搜索。 选择包并在右侧选中 [ ] Project 复选框以安装整个包 点击安装。

您也可以使用 NuGet 命令行

Tools &gt; NuGet package manager &gt; Package manager console下 运行命令Install-Package Newtonsoft.Json -Version 12.0.3

或者像 DerHugo 在 cmets 中指出的那样使用 NuGetForUnity(不是我个人从未使用过 NuGetForUnity,但我听说它很好用)

注意默认情况下这会将包放在/Assets/ 文件夹之外。这有点棘手,因为它有时有效,但有时不起作用(我认为这与 VS 或 Unity 无法在 /Assets/ 目录之外找到包有关)。尤其是在 WebGL 上,这可能会造成问题。

为了解决这个问题,我们会将包移动到 /Assets/ 文件夹内,我们确信 Unity/VS 可以找到它。

导航到已安装的包(位置类似于C:\&lt;user&gt;\&lt;PathToProjectFolder&gt;\YourProject\Packages\Newtonsoft.Json.12.0.3 复制/剪切文件夹 导航到您的资产文件夹并创建一个文件夹\Plugins\,如果它尚不存在 (C:\&lt;user&gt;\&lt;PathToProjectFolder&gt;\YourProject\Assets\Plugins\ 将 Newtonsoft.Json.xx.x.x 粘贴到插件文件夹中。 如果在第 1 步中复制,请务必删除旧位置的插件,以免它两次出现在您的项目中。

您现在可以通过包含 Newtonsoft.Json 命名空间来使用 Newtonsoft 库。

这提供了使用JsonPropertyName 覆盖变量属性名称的功能,例如来自文档:

Videogame starcraft = new Videogame

    Name = "Starcraft",
    ReleaseDate = new DateTime(1998, 1, 1)
;

string json = JsonConvert.SerializeObject(starcraft, Formatting.Indented);

Console.WriteLine(json);
// 
//   "name": "Starcraft",
//   "release_date": "1998-01-01T00:00:00"
// 

还允许您使用 Newtonsoft (de)serializer,它比 Unity 的 JsonUtility 强大得多,提供更多选项,the docs 的基本示例:

Product product = new Product();

product.Name = "Apple";
product.ExpiryDate = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[]  "Small", "Medium", "Large" ;

string output = JsonConvert.SerializeObject(product);
//
//  "Name": "Apple",
//  "ExpiryDate": "2008-12-28T00:00:00",
//  "Price": 3.99,
//  "Sizes": [
//    "Small",
//    "Medium",
//    "Large"
//  ]
//

Product deserializedProduct = JsonConvert.DeserializeObject<Product>(output);

【讨论】:

这绝对是用霰弹枪切蛋糕 - 但它会记住它作为后备选项。 我尝试了 newtonsoft - 虽然它在编辑器中工作,但一旦我导出到 webgl,我就会得到一个“NotSupportedException”。如果你想出了一个技巧,请告诉我,否则我认为这不是一个选择。 我环顾四周,我没有看到那个选项...我假设它会在播放器设置中? 我不久前切换到 4.x 以获得“动态”支持。我想这意味着我将不得不退回到您的原始解决方案:(我真的很感谢您尝试。 @Shadow 检查我的答案的顶部,有一个名为 Newtonsoft.Json-for-Unity 的包我刚刚尝试过,它甚至在 WebGL 中也对我有用。我已经添加了必要的信息以及在哪里可以找到它。

以上是关于如何使用 JSON Utility 读取键中带有连字符的对象?的主要内容,如果未能解决你的问题,请参考以下文章

html怎么读取到字典中带有'_'开头的键

将其字段中带有逗号的 .csv 文件转换为 JSON/TXT

如何使用Javascript读取json文件并将特定键的值写入同一json文件中?

如何使用 Presto JSON 函数访问字段名称中带有“~”的 json 字段

shell处理json串中带空格

如何使用 Pipeline Utility Steps 插件更新现有 yaml 文件的内容