7
$\begingroup$

please forgive me if this is obvious, but I have some JSON:

ExportString[
<|"Names" -> <|
    "Sister" -> "Nina",
    "Brothers" -> {<|"Older" -> "John","Younger" -> "Jake"|>},
    "somethingElse" -> "answer"|>,
  "DOB" -> {<|
        "Nina" -> 2001,
        "location" -> "Miami"|>,
        <|"John" -> 2017,
        "location" -> "Hell"|>}|>, "RawJSON"]

(NOTE, I am converting an association to JSON just for the purposes of this post. In reality I am making a URL request and it is returning JSON).

I want to separate the JSON at depth 1. So I want to return an association of "Names" and an Association of "DOB". Is there a function similar to Lookup that works with JSON or some other way to index JSON?

Thank you.

EDIT: Actually, I would like to keep "Names" as JSON.

EDIT (per comments): Here's what i'm looking for structurally: The first output I want is the JSON corresponding to "Names":

out1 =
{
"Names":{
    "Sister":"Nina",
    "Brothers":[
        {
            "Older":"John",
            "Younger":"Jake"
        }
    ],
    "somethingElse":"answer"
    }
}

The second output I want is an Association of the values corresponding to "DOB":

out2 = 
    <|"DOB"->{<|
        "Nina" -> 2001,
        "location" -> "Miami"|>,
        <|"John" -> 2017,
        "location" -> "Hell"|>}|>
$\endgroup$
5
  • 1
    $\begingroup$ For your simple case, can you please manually type out, what structure you are seeking? Do you want an association that contains JSON as values or something else? $\endgroup$
    – halirutan
    May 12 '17 at 2:10
  • $\begingroup$ It's still not clear what you're looking for. If your association is called a, does ExportString[a["Names"]] do what you want? $\endgroup$
    – yohbs
    May 12 '17 at 3:21
  • $\begingroup$ Also, the title of the question is "How to parse JSON" while the content does not seem to be about parsing at all but rather about constructing JSON from associations. Is that the case? $\endgroup$
    – yohbs
    May 12 '17 at 3:22
  • $\begingroup$ What do you mean "still not clear"? I haven't responded yet. Read the note in the post. I already have the JSON, I want to get the elements out of it. I'll be adding an edit in 2 minutes showing structurally What I'm talking about. $\endgroup$
    – Joe Blow
    May 12 '17 at 3:32
  • $\begingroup$ I just included the ExportString so you could see the JSON I am working with. Ignore it. Just copy and past the original code into Mathematica and the output is where we're starting from. $\endgroup$
    – Joe Blow
    May 12 '17 at 3:48
10
$\begingroup$

The example JSON string:

json = ExportString[<|
    "Names" -> <|
      "Sister" -> "Nina",
      "Brothers" -> {<|"Older" -> "John", "Younger" -> "Jake"|>},
      "somethingElse" -> "answer"
      |>,
    "DOB" -> {
      <|"Nina" -> 2001, "location" -> "Miami"|>,
      <|"John" -> 2017, "location" -> "Hell"|>}
    |>, "RawJSON"];

Import it as an association:

assoc = ImportString[json, "RawJSON"];

Here is how to get an association for each key at the first level:

<|# -> assoc[[#]]|> & /@ Keys[assoc]

Here is how to get a JSON string for each key at the first level:

ExportString[<|# -> assoc[[#]]|>, "JSON"] & /@ Keys[assoc]
$\endgroup$
0
$\begingroup$

The various approaches I have seen on the web, including above, don't work for my data, which may be more complex than others' test cases or contain some unusual or malformed elements. Anyway, I wrote a bit of recursive code that handles it well. I'm posting it here in case it's of use to others.

list2Assoc[in_] := Which[
   VectorQ[in, Head[#] === Rule &], 
   Apply[Association, Map[list2Assoc, in]],
   ListQ[in], Map[list2Assoc, in],
   Head[in] === Rule && ListQ[in[[2]]], in[[1]] -> list2Assoc[in[[2]]],
   True, in];

This converts the JSON, which has been brought into Mathematica with Import and has a Head of List, into an Association of Associations that is easier to extract elements from.

$\endgroup$
6
  • $\begingroup$ GeneralUtilities``ToAssociations does the same thing for you. $\endgroup$ Jan 14 at 6:03
  • $\begingroup$ @Shredderoy How would one use that? $\endgroup$ Jan 14 at 15:45
  • 1
    $\begingroup$ {"a" -> {"b" -> {1, "two"}, "c" -> "three"}, "b" -> 4} // GeneralUtilities``ToAssociations, but with a single backtick instead of two. $\endgroup$ Jan 14 at 16:36
  • $\begingroup$ @Shredderroy doesn't work for me. GeneralUtilities and GeneralUtilities`ToAssociations are blue in the front end and do nothing. $\endgroup$ Jan 14 at 17:15
  • $\begingroup$ The expression is blue for me too. But it works. I have version 12.2.0, but it has worked since early version 12. Too bad I cannot post a picture in a comment. $\endgroup$ Jan 14 at 17:32
0
$\begingroup$

Here is another take on this issue. I was importing a nested JSON from an API call from the Homeland Infrastructure Foundation using the call below:

tst = Import["https://services1.arcgis.com/Hp6G80Pky0om7QvQ/arcgis/rest/services/Electric_Power_Transmission_Lines/FeatureServer/0/query?where=1%3D1&outFields=*&geometry=-123.936%2C35.284%2C-90.559%2C41.313&geometryType=esriGeometryEnvelope&inSR=4326&spatialRel=esriSpatialRelIntersects&outSR=4326&f=json", "JSON"];

My approach was to find the List heads that had a rule within them using:

assocConversionF[list_] :=If[Length[Position[list, Rule, {2}]] > 0, Association[list], list]

listL = ReverseSortBy[Union[DeleteCases[Position[tst, List][[All, ;; -2]], {}]], Length@# &]

Association[MapAt[assocConversionF, tst, listL]]

I hope this helps someone with a similar issue.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.