6
$\begingroup$

My question is about a XML document and finding parts of strings.

test = Import["C:\\Users\\filename.xml", "XMLObject"]

This gives the following output:

XMLObject["Document"][{XMLObject["Declaration"]
   ["Version" -> "1.0", "Encoding" -> "UTF-8", "Standalone" -> "yes"]},
  XMLElement["file", {"name" -> "filename.xml"},
   {XMLElement["tekst", {"write" -> "test1"},
     {XMLElement["relation", {"type" -> "co-synonyms", "freq" -> "3"},
       {XMLElement["lu", {"sense" -> "2", "id" -> "AA-504771"}, {}],
        XMLElement["lu", {"sense" -> "1", "id" -> "BB-59362"}, {}],
        XMLElement["lu", {"sense" -> "1", "id" -> "CC-59362"}, {}]}]}],
    XMLElement["tekst", {"write" -> "test2"},
     {XMLElement["relation", {"type" -> "co-synonyms", "freq" -> "2"},
       {XMLElement["lu", {"sense" -> "2", "id" -> "AA-20210"}, {}],
        XMLElement["lu", {"sense" -> "3", "id" -> "BB-30211"}, {}]}]}],
    XMLElement["tekst", {"write" -> "test3"},
     {XMLElement["relation", {"type" -> "co-synonyms", "freq" -> "2"},
       {XMLElement["lu", {"sense" -> "2", "id" -> "AA-445227"}, {}],
        XMLElement["lu", {"sense" -> "1", "id" -> "BB-629609"}, {}]}]}],
    XMLElement["tekst", {"write" -> "test4"},
     {XMLElement["relation", {"type" -> "co-synonyms", "freq" -> "2"},
       {XMLElement["lu", {"sense" -> "1", "id" -> "AA-7799"}, {}],
        XMLElement["lu", {"sense" -> "2", "id" -> "BB-407682"}, {}]}]}]}], {}]

The XML has a fixed structure. I want the combination of the XML where "write" -> "test1" and "id" -> "BB-59362"

The desired output is:

{test1, {AA-504771,BB-59362,CC-59362}}, test2, {AA-20210, BB-30211}, test3, {AA-445227,BB-629609}, test4, {AA-7799, BB-407682}}

I tried several syntaxes with, for example, XMLElement. The result in each case was output with XML. Does anyone have a suggestion on how to solve this problem?

$\endgroup$
4
$\begingroup$

This seems to be a normal way to extract the data:-

xml = XMLObject["Document"][{XMLObject["Declaration"]
     ["Version" -> "1.0", "Encoding" -> "UTF-8", 
     "Standalone" -> "yes"]},
   XMLElement["file", {"name" -> "filename.xml"},
    {XMLElement["tekst", {"write" -> "test1"},
      {XMLElement["relation", {"type" -> "co-synonyms", "freq" -> "3"},
        {XMLElement["lu", {"sense" -> "2", "id" -> "AA-504771"}, {}],
         XMLElement["lu", {"sense" -> "1", "id" -> "BB-59362"}, {}],
         XMLElement["lu", {"sense" -> "1", "id" -> "CC-59362"}, {}]}]}],
     XMLElement["tekst", {"write" -> "test2"},
      {XMLElement["relation", {"type" -> "co-synonyms", "freq" -> "2"},
        {XMLElement["lu", {"sense" -> "2", "id" -> "AA-20210"}, {}],
         XMLElement["lu", {"sense" -> "3", "id" -> "BB-30211"}, {}]}]}],
     XMLElement["tekst", {"write" -> "test3"},
      {XMLElement["relation", {"type" -> "co-synonyms", "freq" -> "2"},
        {XMLElement["lu", {"sense" -> "2", "id" -> "AA-445227"}, {}],
         XMLElement["lu", {"sense" -> "1", "id" -> "BB-629609"}, {}]}]}],
     XMLElement["tekst", {"write" -> "test4"},
      {XMLElement["relation", {"type" -> "co-synonyms", "freq" -> "2"},
        {XMLElement["lu", {"sense" -> "1", "id" -> "AA-7799"}, {}],
         XMLElement[
          "lu", {"sense" -> "2", "id" -> "BB-407682"}, {}]}]}]}],
   {}];

tekstcases = Cases[xml, XMLElement["tekst", __], \[Infinity]];

tests = Map[# /. XMLElement["tekst", {___, "write" -> x_}, ___] :> x &, tekstcases];

idcases = Cases[#, XMLElement["lu", __], \[Infinity]] & /@ tekstcases;

ids = Map[# /. XMLElement["lu", {___, "id" -> x_}, ___] :> x &, idcases];

Flatten[Transpose[{tests, ids}], 1]

{test1, {AA-504771, BB-59362, CC-59362}, test2, {AA-20210, BB-30211}, test3, {AA-445227, BB-629609}, test4, {AA-7799, BB-407682}}

Using replacements (/.) requires that every 'tekst' record has a 'write' and an 'id', otherwise Cases should be used. But if the data is regularly structured replacements will be fine.

$\endgroup$
4
$\begingroup$

In this case what you want is simple/regular enough that you can define the XMLElement etc. as functions that return what you want inside a Block:

Block[{XMLObject, XMLElement},
 XMLObject["Document"] = #2 &;
 XMLElement["tekst", {"write" -> t_}, data_] := {t, Sequence @@ data};
 XMLElement["lu", attr_, data_] := "id" /. attr;
 XMLElement[tag_, attr_, data_] := data;
 Flatten[test, 1]]
(*
  {"test1", {"AA-504771", "BB-59362", "CC-59362"},
   "test2", {"AA-20210",  "BB-30211"},
   "test3", {"AA-445227", "BB-629609"},
   "test4", {"AA-7799", "BB-407682"}}
*)

Omit the Flatten if you want the "write" paired with the "id".

$\endgroup$
  • $\begingroup$ Had a similar problem. Thanks! $\endgroup$ – user12572 Oct 7 '15 at 1:30
2
$\begingroup$

The following can be used to extract the identifiers used in a given test case, which matches the "write" attribute of an individual XMLElement. Note that the attribute names "write" and "id" are hard-coded, which is not ideal.

GetIdentifiers[data_, test_String] := Module[{elem, attr},
  (*find test case*)
  elem = Cases[data, 
    XMLElement[___, {___, "write" -> test, ___}, ___], \[Infinity]];
  (*get selected xml attribute*)
  attr = Cases[elem, 
    XMLElement[___, {___, "id" -> attrib_, ___}, ___] :> 
     attrib, \[Infinity]];
  {test, attr}
  ]

GetIdentifiers[symbolicXML, "test1"]

{"test1", {"AA-504771", "BB-59362", "CC-59362"}}

$\endgroup$
2
$\begingroup$

Assuming that the example XML shown is representative, then we can exploit the fact the write and id attribute names are unambiguous within tekst elements:

Cases[test
, XMLElement["tekst", a_, c_] :> Sequence["write" /. a, Cases[c, ("id" -> i_) :> i, -1]]
, -1
]

(*
{ "test1", {"AA-504771", "BB-59362", "CC-59362"}, "test2", {"AA-20210", "BB-30211"},
  "test3", {"AA-445227", "BB-629609"}, "test4", {"AA-7799", "BB-407682"} }
*)

Sequence could be replaced with List or Rule if it is preferable to keep the write / id groups together. For example, using Rule:

Cases[test
, XMLElement["tekst", a_, c_] :> Rule["write" /. a, Cases[c, ("id" -> i_) :> i, -1]]
, -1
]

(*
{ "test1"->{"AA-504771", "BB-59362", "CC-59362"}, "test2"->{"AA-20210", "BB-30211"},
  "test3"->{"AA-445227", "BB-629609"}, "test4"->{"AA-7799", "BB-407682"} }
*)
$\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.