改变 JSON 文件中的键,同时保留该括号部分内的数据

huangapple go评论58阅读模式
英文:

Changing the key of JSON file whilst retaining data within that bracket section

问题

我的第一个想法是简单地“搜索和替换底层的JSON代码,逐行检查它。这种方法不适用,因为相同的“关键词”可能在不同地方使用,但只有一行需要更改。

然后我想,读取现有数据,创建一个新条目,将数据推送到该块,然后删除原始键。前者可以工作,但原始数据和其所有数据仍然保持不变。

这里有一个简化版本(不认为超过500行会被欣赏):

{
  "project 1": {
    "ad_campaign": {
      "ad_1": {
        "data1",
        "data2"
      },
      "ad_2": {
        "data1",
        "data2"
      }
    }
  },
  "project 2": {
    "ad_campaign": {
      "ad_1": {
        "data1",
        "data2"
      },
      "ad_201": {
        "data1",
        "data2"
      }
    }
  }
}

“data”条目每个广告6行,每个项目可能有几十个广告,几十个项目!!

他们想要将Projec1的“ad_1”更改为“ad_101”。

正如你所看到的,两个项目都有名为“ad_1”的广告,所以简单地搜索和替换底层文本代码文件是行不通的。

如果我使用$project1->{"ad_campaign"},我可以获取所有数据。然后,如果我使用:$project1->{"ad_campaign"} = $new_ad_id ...它会创建一个新的部分(然后我可以逐行复制数据)... 但我仍然无法摆脱原始的“ad_1”条目!($project1->{"ad_campaign"}->{"ad_1"} = null(不允许裸词)/ "null" / "" 没有效果 - 即使在尝试先删除内部数据时。这可能导致data1:'', data2:''等)

我真正需要的是一种方法来执行$customer1->{"ad_campaign"}->{"new_ad_id"},以简单地更改第三个键。 (我已经搜索了“更改”/“替换”JSON键,但没有结果...尤其是因为我正在使用PERL)

再加入“工程中的其他问题”;文件以单行方式保存(使用JSON::PP,“pretty”似乎添加了太多空格 - 制表符缩进似乎更好),因此很难分解并逐行扫描。 (另一个想法是重新迭代文件,一旦找到“customer1”,再进行另一个循环,并在找到“ad_campaign”时设置另一个标志,然后最后的循环寻找“ad_1”。这似乎有点低效...而且所有解码的数据都在一个哈希中!)

请记住,我已经简化了这段代码。在“Project1”和“ad_campaign”之间可能有另外50个键值对PER PROJECT

可能有一种通过哈希“参数”找到/更改值的简单解决方案...但我还没有找到它!

  • 在别处我读到:delete $JSON->{"project1"}->{"ad_campaign"}->{"ad_id"},但那也没有删除原始数据!

我刚刚想到一个封闭的思想:也许我可以做一些类似于“indexOf”的事情来定位project1/ad_campaign等,然后进行split/splice,推送数据的一半,然后再次拆分等,直到找到要替换的单词。但再次,这似乎对于一个可能是一个相当基本的问题来说有点过度。

英文:

My first thought was a simple "search and replace of the underlying JSON code, inspecting it line by line. That will not work as the same "keyword" could be used in various places, but only one specific line needs to be changed.

Then I thought, read the existing data in, create a new entry, push the data into that block, and then delete the original key. The former works ... but leaves the original and all its data intact.

Here is a simplified version (don't think 500+ lines would be appreciated):

{
project 1:{
     ad_campaign:{
       ad_1{
          data1,
          data2
       },
       ad_2{
         data1,
         data2
       }
     }
}
project 2:{
     ad_campaign:{
     ad_1{
        data1,
        data2
        }
     ad_201{
       data1,
       data2
       }
     }
}
}

The "data" entries are 6 lines per ad, and there could be dozens of ads per project, and dozens of projects !!

They want to change Projec1 "ad_1" to "ad_101"

As you can see, both projects have ads named "ad_1", so a simple search and replace of the underlying text code file is a no-go.

If I use $project1->{"ad_campaign"}, I can get all the data. If I then use: $project1->{"ad_campaign"}=$new_ad_id ... it creates a new section (which I =could= then copy the data line - by - line) ... but I still cannot get rid of the original "ad_1" entry! ($project1->{"ad_campaign"}->{"ad_1"} =null (no bareword allowed) / "null" / "" have no effect - even when trying to remove the internal data first. That could result in data1:'',data2:'' etc)

What I really need is a way to do $customer1->{"ad_campaign"}->{"new_ad_id"} to simply change that third key. (I've Googled "change" / "replace" JSON key, but no results ... especially as I am using PERL)

One more "spanner in the works"; the file gets saved as a single line, (Using JSON::PP The "pretty" seems to add too many spaces - tab indents would seem better) so would be difficult to break apart, and do a line by line scan anyway. (Another idea was to reiterate through file, set a flag once hit "customer1", another loop, and another flag for "ad_campaign", and then final loop seeking "ad_1". Seems a bit inefficient ... plus all the decoded data is in a hash anyway!)

Please bear in mind, I have simplified this code. Between "Project1" and "ad_campaign" could be another 50 key:value pairs PER PROJECT

There is possibly some simple solution finding / changing the value via a hash "argument" ... but I've yet to find it!

  • Somewhere else I read: delete $JSON->{"project1"}->{"ad_campaign"}->{"ad_id"} but that didn't delete the original either!

Just had a closing thought: Maybe I could do something like "indexOf" to locate project1/ad_campaign etc, and then do split/splice, pushing one half of data, then split again etc until I find the word to replace. But again, that does seem overkill for what could be a pretty basic problem

答案1

得分: 4

use strict;
use warnings;
use feature 'say';

use JSON;

my $data = <<EOM;
{
    "project 1": {
      "ad_campaign": {
        "ad_1": [
          "p1: ad:1 data1",
          "p1: ad:1 data2"
        ],
        "ad_2": [
          "p1: ad:2 data1",
          "p2: ad:2 data2"
        ]
      }
    },
    "project 2": {
      "ad_campaign": {
        "ad_1": [
          "p2: ad:1 data1",
          "p2: ad:1 data2"
        ],
        "ad_201": [
          "p2: ad:201 data1",
          "p2: ad:201 data2"
        ]
      }
    }
  }
EOM

my $in = from_json($data);

# create "ad_101" to reference to the existing "ad_1" data tree
$in->{"project 1"}{"ad_campaign"}{"ad_101"} = $in->{"project 1"}{"ad_campaign"}{"ad_1"};

# now delete the reference to "ad_1"
delete $in->{"project 1"}{"ad_campaign"}{"ad_1"};

# using pretty & canonical here to make the results easier to read
# don't use pretty if you want all JSON output as a single line
say to_json($in, {pretty => 1, canonical => 1});
英文:

Try code below.


use strict ;
use warnings;
use feature &#39;say&#39;;

use JSON;

my $data = &lt;&lt;EOM;
{
    &quot;project 1&quot;: {
      &quot;ad_campaign&quot;: {
        &quot;ad_1&quot;: [
          &quot;p1: ad:1 data1&quot;,
          &quot;p1: ad:1 data2&quot;
        ],
        &quot;ad_2&quot;: [
          &quot;p1: ad:2 data1&quot;,
          &quot;p2: ad:2 data2&quot;
        ]
      }
    },
    &quot;project 2&quot;: {
      &quot;ad_campaign&quot;: {
        &quot;ad_1&quot;: [
          &quot;p2: ad:1 data1&quot;,
          &quot;p2: ad:1 data2&quot;
        ],
        &quot;ad_201&quot;: [
          &quot;p2: ad:201 data1&quot;,
          &quot;p2: ad:201 data2&quot;
        ]
      }
    }
  }
EOM

my $in = from_json($data);

# create &quot;ad_101&quot; to reference to the existing &quot;ad_1&quot; data tree
$in-&gt;{&quot;project 1&quot;}{&quot;ad_campaign&quot;}{&quot;ad_101&quot;} = $in-&gt;{&quot;project 1&quot;}{&quot;ad_campaign&quot;}{&quot;ad_1&quot;};

# now delete the reference to &quot;ad_1&quot;
delete $in-&gt;{&quot;project 1&quot;}{&quot;ad_campaign&quot;}{&quot;ad_1&quot;};


# using pretty &amp; canonical here to make the results easier to read
# don&#39;t use pretty if you want all JSON output as a single line
say to_json($in, {pretty =&gt; 1, canonical =&gt; 1});

output is

{
   &quot;project 1&quot; : {
      &quot;ad_campaign&quot; : {
         &quot;ad_101&quot; : [
            &quot;p1: ad:1 data1&quot;,
            &quot;p1: ad:1 data2&quot;
         ],
         &quot;ad_2&quot; : [
            &quot;p1: ad:2 data1&quot;,
            &quot;p2: ad:2 data2&quot;
         ]
      }
   },
   &quot;project 2&quot; : {
      &quot;ad_campaign&quot; : {
         &quot;ad_1&quot; : [
            &quot;p2: ad:1 data1&quot;,
            &quot;p2: ad:1 data2&quot;
         ],
         &quot;ad_201&quot; : [
            &quot;p2: ad:201 data1&quot;,
            &quot;p2: ad:201 data2&quot;
         ]
      }
   }
}

Key point to note is that nested hashes in perl use references, which are akin to pointers in C/C++ land. Means that after running this line

$in-&gt;{&quot;project 1&quot;}{&quot;ad_campaign&quot;}{&quot;ad_101&quot;} = $in-&gt;{&quot;project 1&quot;}{&quot;ad_campaign&quot;}{&quot;ad_1&quot;};

both the ad_1 and ad_101 keys reference the same data, namely

[
   &quot;p1: ad:1 data1&quot;,
   &quot;p1: ad:1 data2&quot;
],

[EDIT - answering some of the comments]

I changed the data values to highlight that the reference in ad_1 and ad_101 are pointing to unique data in the JSON document. You didn't supply any data, so I made some up.

Use of from_json and to_json rather than decode_json & encode_json is purely to allow the pretty parameter to be controlled. Use the variant that suits your use-case. Check out the docs here

答案2

得分: 2

另一个选项是使用 jq,而不是Perl

假设 data.json 包含以下内容

{
    &quot;project 1&quot;: {
      &quot;ad_campaign&quot;: {
        &quot;ad_1&quot;: [
          &quot;p1: ad:1 data1&quot;,
          &quot;p1: ad:1 data2&quot;
        ],
        &quot;ad_2&quot;: [
          &quot;p1: ad:2 data1&quot;,
          &quot;p2: ad:2 data2&quot;
        ]
      }
    },
    &quot;project 2&quot;: {
      &quot;ad_campaign&quot;: {
        &quot;ad_1&quot;: [
          &quot;p2: ad:1 data1&quot;,
          &quot;p2: ad:1 data2&quot;
        ],
        &quot;ad_201&quot;: [
          &quot;p2: ad:201 data1&quot;,
          &quot;p2: ad:201 data2&quot;
        ]
      }
    }
}

执行以下一行命令来进行重命名

$ jq &#39;.&quot;project 1&quot;.ad_campaign |= ( .ad_101 = .ad_1 | del(.ad_1) ) &#39;  data.json 

输出为

{
  &quot;project 1&quot;: {
    &quot;ad_campaign&quot;: {
      &quot;ad_2&quot;: [
        &quot;p1: ad:2 data1&quot;,
        &quot;p2: ad:2 data2&quot;
      ],
      &quot;ad_101&quot;: [
        &quot;p1: ad:1 data1&quot;,
        &quot;p1: ad:1 data2&quot;
      ]
    }
  },
  &quot;project 2&quot;: {
    &quot;ad_campaign&quot;: {
      &quot;ad_1&quot;: [
        &quot;p2: ad:1 data1&quot;,
        &quot;p2: ad:1 data2&quot;
      ],
      &quot;ad_201&quot;: [
        &quot;p2: ad:201 data1&quot;,
        &quot;p2: ad:201 data2&quot;
      ]
    }
  }
}

要让 jq 输出为单行,请在命令行中添加 -c 选项

$ jq -c  &#39;.&quot;project 1&quot;.ad_campaign |= ( .ad_101 = .ad_1 | del(.ad_1) ) &#39;  data.json 
{&quot;project 1&quot;:{&quot;ad_campaign&quot;:{&quot;ad_2&quot;:[&quot;p1: ad:2 data1&quot;,&quot;p2: ad:2 data2&quot;],&quot;ad_101&quot;:[&quot;p1: ad:1 data1&quot;,&quot;p1: ad:1 data2&quot;]}},&quot;project 2&quot;:{&quot;ad_campaign&quot;:{&quot;ad_1&quot;:[&quot;p2: ad:1 data1&quot;,&quot;p2: ad:1 data2&quot;],&quot;ad_201&quot;:[&quot;p2: ad:201 data1&quot;,&quot;p2: ad:201 data2&quot;]}}
英文:

Another option is to use jq, rather than Perl

Assuming data.json contains this

{
    &quot;project 1&quot;: {
      &quot;ad_campaign&quot;: {
        &quot;ad_1&quot;: [
          &quot;p1: ad:1 data1&quot;,
          &quot;p1: ad:1 data2&quot;
        ],
        &quot;ad_2&quot;: [
          &quot;p1: ad:2 data1&quot;,
          &quot;p2: ad:2 data2&quot;
        ]
      }
    },
    &quot;project 2&quot;: {
      &quot;ad_campaign&quot;: {
        &quot;ad_1&quot;: [
          &quot;p2: ad:1 data1&quot;,
          &quot;p2: ad:1 data2&quot;
        ],
        &quot;ad_201&quot;: [
          &quot;p2: ad:201 data1&quot;,
          &quot;p2: ad:201 data2&quot;
        ]
      }
    }
}

this one-liner to do the rename

$ jq &#39;.&quot;project 1&quot;.ad_campaign |= ( .ad_101 = .ad_1 | del(.ad_1) ) &#39;  data.json 

output is

{
  &quot;project 1&quot;: {
    &quot;ad_campaign&quot;: {
      &quot;ad_2&quot;: [
        &quot;p1: ad:2 data1&quot;,
        &quot;p2: ad:2 data2&quot;
      ],
      &quot;ad_101&quot;: [
        &quot;p1: ad:1 data1&quot;,
        &quot;p1: ad:1 data2&quot;
      ]
    }
  },
  &quot;project 2&quot;: {
    &quot;ad_campaign&quot;: {
      &quot;ad_1&quot;: [
        &quot;p2: ad:1 data1&quot;,
        &quot;p2: ad:1 data2&quot;
      ],
      &quot;ad_201&quot;: [
        &quot;p2: ad:201 data1&quot;,
        &quot;p2: ad:201 data2&quot;
      ]
    }
  }
}

to get jq to output as a single line, add the -c option to the commandline

$ jq -c  &#39;.&quot;project 1&quot;.ad_campaign |= ( .ad_101 = .ad_1 | del(.ad_1) ) &#39;  data.json 
{&quot;project 1&quot;:{&quot;ad_campaign&quot;:{&quot;ad_2&quot;:[&quot;p1: ad:2 data1&quot;,&quot;p2: ad:2 data2&quot;],&quot;ad_101&quot;:[&quot;p1: ad:1 data1&quot;,&quot;p1: ad:1 data2&quot;]}},&quot;project 2&quot;:{&quot;ad_campaign&quot;:{&quot;ad_1&quot;:[&quot;p2: ad:1 data1&quot;,&quot;p2: ad:1 data2&quot;],&quot;ad_201&quot;:[&quot;p2: ad:201 data1&quot;,&quot;p2: ad:201 data2&quot;]}}}

答案3

得分: 2

以下是已经翻译好的部分:

  • The others have answered this question adequately, and I'd also choose jq (see Using jq how can I replace the name of a key with something else and the jq FAQ).

  • But, I want to emphasis what @pmqs is doing but not yelling from the rooftops.

    • 但是,我想强调 @pmqs 在做什么,但不是在屋顶大声喊叫。
  • It's just Perl

    • 这只是 Perl
  • If you want to do something in Perl, you basically have scalars, arrays, and hashes. If you are already in Perl land, that's what you are manipulating.

    • 如果你想在 Perl 中做某事,你基本上有标量、数组和哈希。如果你已经在 Perl 领域,这就是你正在操作的内容。
  • You don't change a JSON key in Perl; you change a hash key. It doesn't matter that you got that hash from JSON or will output it as JSON. That's how you get to @pmqs's delete:

    • 你不是在 Perl 中改变 JSON 键;你是在改变哈希键。无论你是从 JSON 中获取了这个哈希还是将其输出为 JSON,都没有关系。这就是你到达 @pmqs 的 delete 的方式:

      $hash->{$new_key} = delete $hash->{$old_key};

  • Everything beyond that is just input and output.

    • 其他一切只是输入和输出。
  • The right tool for the right job

    • 适合工作的正确工具
  • You have a few other questions about the output, and those questions are unrelated to changing a hash key. When I deal with this, I simply ignore it. If it outputs it all on one line, I don't really care. I can get pretty output with jq:

    • 你还有一些关于输出的其他问题,这些问题与改变哈希键无关。当我处理这个问题时,我只是忽略它。如果它将所有内容都输出在一行上,我并不真的在乎。我可以使用 jq 获得漂亮的输出:

      % jq .

  • If I want to minify it, I can use testing:

    • 如果我想要缩小它,我可以使用 testing

      % jq -r tostring data.json

  • You mentioned that you didn't like the indention. That's easy to change. jq uses 2 space indents, but I can make that smaller (or larger, or tabs) (see also How to restrict indentation while using jq filters on json file):

  • You say that you can't use jq because you are using Perl, but remember, Perl is the glue language of the internet. You can easily call other programs to do your work and you are expected to do so when something else can do the job better. Why wrestle with Perl and its modules when an existing tool already does a better job? If you aren't able to install it for whatever reason, that's fine. But saying that you can only choose one tool unnecessarily limits you.

    • 你说你不能使用 jq,因为你正在使用 Perl,但请记住,Perl 是互联网的胶水语言。你可以轻松地调用其他程序来完成你的工作,而且当其他工具可以做得更好时,你应该这样做。为什么要与 Perl 及其模块纠缠,当一个现成的工具已经做得更好了呢?如果由于某种原因你无法安装它,那没关系。但说你只能选择一个工具会不必要地限制你。
  • Setting aside that, JSON::PP has settings for space_before, space_after, indent, and indent_length. You can adjust those how you like. If you have problems with those, you can ask a separate question.

    • 撇开这些不谈,JSON::PP 提供了 space_beforespace_afterindentindent_length 的设置。你可以根据自己的喜好进行调整。如果你对这些设置有问题,你可以提出一个单独的问题。
英文:

The others have answered this question adequately, and I'd also choose jq (see Using jq how can I replace the name of a key with something else and the jq FAQ).

But, I want to emphasis what @pmqs is doing but not yelling from the rooftops.

It's just Perl

If you want to do something in Perl, you basically have scalars, arrays, and hashes. If you are already in Perl land, that's what you are manipulating.

You don't change a JSON key in Perl; you change a hash key. It doesn't matter that you got that hash from JSON or will output it as JSON. That's how you get to @pmqs's delete:

$hash-&gt;{$new_key} = delete $hash-&gt;{$old_key};

Everything beyond that is just input and output.

The right tool for the right job

You have a few other questions about the output, and those questions are unrelated to changing a hash key. When I deal with this, I simply ignore it. If it outputs it all on one line, I don't really care. I can get pretty output with jq:

% jq .

If I want to minify it, I can use testing:

% jq -r tostring data.json

You mentioned that you didn't like the indention. That's easy to change. jq uses 2 space indents, but I can make that smaller (or larger, or tabs) (see also How to restrict indentation while using jq filters on json file):

% jq --indent 1 . data.json

You say that you can't use jq because you are using Perl, but remember, Perl is the glue language of the internet. You can easily call other programs to do your work and you are expected to do so when something else can do the job better. Why wrestle with Perl and its modules when an existing tool already does a better job? If you aren't able to install it for whatever reason, that's fine. But saying that you can only choose one tool unnecessarily limits you.

Setting aside that, JSON::PP has settings for space_before, space_after, indent, and indent_length. You can adjust those how you like. If you have problems with those, you can ask a separate question.

huangapple
  • 本文由 发表于 2023年2月18日 21:31:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/75493678.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定