Przejdź do głównej treści

TreeStructInfo tester

Logo TreeStructInfo

TreeStructInfo → JSON, PHP, XML lub YAML w jednej chwili!

Potrzebujesz szybko sparsować plik w formacie TreeStructInfo i przekształcić go w inną czytelną strukturę? Skorzystaj z mojej lekkiej aplikacji do błyskawicznego przetwarzania tego formatu.

Wczytaj plik .tsinfo lub wklej treść
Automatyczny parser formatu TreeStructInfo 2.0
Konwersja do JSON, PHP, XML lub YAML
Obsługa atrybutów, węzłów, typów i referencji
Gotowy kod w jednej z wybranych struktur – do użycia od ręki

Idealne narzędzie dla programistów pracujących z tym formatem lub integrujących dane z zewnętrznych źródeł.

Sprawdź, jak prosto można przejść z pliku tekstowego do innego gotowego formatu!

Ważne
  • w pliku konfiguracyjnym musi być zachowana odpowiednia kolejność definiowania danych
  • atrybuty i węzły referencjonowane muszą być zdefiniowane po całym drzewie z danymi czyli po end tree
  • atrybuty referencjonowane muszą być zdefiniowane przed węzłami referencjonowanymi
  • węzły referencjonowane występujące w innych węzłach referencjonowanych muszą być zdefiniowane przed nimi
  • przy włączonym automatycznym rzutowaniu typów separatorem miejsc dziesiętnych musi być kropka
  • maksymalny oczekiwany czas wykonania wynosi 10ms (do celów wizualizacji paskiem postępu i jego kolorami)
  • dla parsera wcięcia i ich rozmiar nie mają znaczenia
Ostrzeżenie

Ta aplikacja służy wyłącznie do testowania parsera TreeStructInfo. Parser może konwertować dane dając nieprawidłowe wyniki, zwłaszcza przy ustawionym automatycznum rzutowaniu typów albo jednego znaku łączenia stringów który jest jeden dla wszystkich danych.

Strona formatu TreeStructInfo:
https://tsinfo.4programmers.net/pl/index.htm

Kody źródłowe parsera użyte w tej aplikacji:
http://www.dariuszrorat.ugu.pl/blog/wpis/96-biblioteka-treestructinfo-php
http://www.dariuszrorat.ugu.pl/blog/wpis/98-biblioteka-treestructinfo-javascript

Dokumentacja parsera:
http://www.dariuszrorat.ugu.pl/dokumenty/14-dokumentacja-treestructinfoparser-php
http://www.dariuszrorat.ugu.pl/dokumenty/16-dokumentacja-treestructinfoparserjs

Podświetlenie składni użyte w tej aplikacji:
http://www.dariuszrorat.ugu.pl/blog/wpis/97-podswietlenie-skladni-formatu-treestructinfo-codemirror-i-highlightjs

Przykład do testowania

:: Example TreeStructInfo 2.0
treestructinfo "2.0" name "Test Sample Tree"
  :: Various root tree attributes
  attr StringAttr "Example String Attribute"
  attr BoolAttr "true"
  attr IntBoolAttr "1"
  attr IntAttr "42"
  attr FloatAttr "3.1415"
  attr EngineeringAttr "1.23E+03"
  attr BinaryDataAttr "54726565537472756374496E666F202D"
  attr Base64DataAttr "U29tZSBiYXNlNjQgZGF0YQ=="
  attr MultilineDataAttr "First Line"
                         "Second Line"
  node Coords
    attr DecCoords "100,120"
    attr HexCoords "0xA0,0x80"
    attr OctCoords "0o200,0o100"
    attr BinCoords "0b10110011,0b11001101"
  end node
  node Meta
    attr Author "John Doe"
    ref attr SharedNote
    ref node ParentRefNode
  end node
  node Items
    node ItemA
      attr Price "12,50 zł"
      attr Available "false"
    end node
    node ItemB
      attr Price "20,00 zł"
      attr Available "true"
    end node
  end node
end tree
:: Ref attributes and ref nodes must be defined after end tree
:: Ref attrubites must be defined before ref nodes
ref attr SharedNote "This is a shared note used across the tree."
:: Child ref node must by defined before parent ref node
ref node ChildRefNode
  attr Name "This Is Child Ref Node"
  node ChildNode
    attr ExampleValue "123456"
  end node
end ref node

ref node ParentRefNode
  attr Name "This Is Parent Ref Node"
  node Child
    attr ChildExampleAttr "This is child node attr"
    ref attr SharedNote
  end node
  ref node ChildRefNode
end ref node

Wynik

{{ result }}
{{time}} ms

Kod po stronie przeglądarki

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.7/codemirror.min.css">
<link rel="stylesheet" href="http://www.dariuszrorat.ugu.pl/assets/css/codemirror/theme/dark.css">
<link rel="stylesheet" href="http://www.dariuszrorat.ugu.pl/assets/css/codemirror/themes.css">
<div id="app">
    <div class="row mb-3">
        <div class="col-12">
            <label for="tsinfo" class="form-label">Tekst TreeStructInfo</label>
            <textarea id="tsinfo" class="form-control"></textarea>                    
        </div>
    </div>
    <div class="row mb-3">
        <div class="col-md-4">
            <label for="engine" class="form-label">Silnik parsowania</label>
            <select id="engine" class="form-select" v-model="engine" @change="engineChange">                
                <option value="php">PHP</option>
                <option value="javascript">JavaScript</option>
            </select>                    
        </div>
        <div class="col-md-4">
            <label for="format" class="form-label">Format wyjściowy</label>
            <select id="format" class="form-select" v-model="format" :disabled="engine === 'javascript'">                
                <option value="json">JSON</option>
                <option value="php">PHP</option>
                <option value="xml">XML</option>
                <option value="yaml">YAML</option>
            </select>                    
        </div>
        <div class="col-md-4">
            <label for="joinChar" class="form-label">Łączenie stringów</label>
            <select id="joinChar" class="form-select" v-model="joinChar">                
                <option value="empty">bez spacji</option>
                <option value="space">spacja</option>
                <option value="eol">nowa linia</option>
            </select>                    
        </div>
    </div>
    <div class="row mb-3">
        <div class="col-12">
            <div class="form-check form-switch">
                <input type="checkbox" id="casting" name="casting" class="form-check-input" v-model="casting" true-value="on" false-value="off">                    
                <label class="form-check-label" for="casting">automatyczne rzutowanie typów</label>
            </div>
        </div>        
        <div class="col-12">
            <div class="form-check form-switch">
                <input type="checkbox" id="val01asBool" name="val01asBool" class="form-check-input" v-model="val01asBool" true-value="on" false-value="off">                    
                <label class="form-check-label" for="val01asBool">wartości [0, 1] jako boolean</label>
            </div>
        </div>                
        <div class="col-12">
            <div class="form-check form-switch">
                <input type="checkbox" id="rawdata" name="rawdata" class="form-check-input" v-model="rawdata" true-value="on" false-value="off">                    
                <label class="form-check-label" for="rawdata">tylko surowe dane bez nazwy i wersji formatu</label>
            </div>
        </div>                
    </div>
    <div class="mb-3">
        <button class="btn btn-primary" @click="convert"><i class="bi bi-play-fill"></i> Wykonaj</button>
    </div>    
    <div class="mb-3" v-if="success">        
        <h2>Wynik</h2>
        <pre><code :class="'language-'+resultLng" id="output">{{ result }}</code></pre>
    </div>   
    <div class="progress mb-3" v-if="success">
        <div class="progress-bar" role="progressbar" :style="'width: '+ percent + '%'" :class="bgColor">
        {{time}} ms
        </div>
    </div>

</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.7/codemirror.min.js"></script>
<script src="http://www.dariuszrorat.ugu.pl/assets/js/codemirror/mode/tsinfo/tsinfo.js"></script>
<script src="http://www.dariuszrorat.ugu.pl/assets/js/highlightjs/languages/tsinfo.js" defer></script>
<script src="http://www.dariuszrorat.ugu.pl/assets/js/tsinfo/parser.js"></script>
<script>
// Max 10ms expected time    
const MAX_EXPECTED_TIME = 10;
function escapeHTML(str) {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

var vm = null;
document.addEventListener('DOMContentLoaded', function() {    
vm = new Vue({
    el: '#app',
    data: {
        tsinfo: '',
        engine: 'php',
        format: 'json',
        joinChar: 'empty',
        casting: 'on',
        val01asBool: 'off',
        rawdata: 'off',
        result: '',
        resultLng: 'json',
        time: 0,
        bgColor: 'text-bg-primary',
        percent: 0,
        success: false,
        parser: null
    },
    mounted() {
        var self = this;
        this.$nextTick(function () {
            self.parser = new TreeStructInfoParserJS();
        });
    },        
    updated() {                     
        this.$nextTick(function () {
            const el = document.getElementById('output');                        
            if (el)
            {            
                if (el.hasAttribute('data-highlighted'))
                    el.removeAttribute('data-highlighted');
                if (el.hasAttribute('data-highlighter'))
                    el.removeAttribute('data-highlighter');
                if (el.classList.contains('hljs'))
                    el.classList.remove(...Array.from(el.classList).filter(cls => cls.startsWith('hljs')));
                el.innerHTML = escapeHTML(el.textContent);
                hljs.highlightElement(el);
            }
        });            
    },    
    methods: {
       engineChange() {
           var self = this;
           if (self.engine === 'javascript')
           {
               self.format = 'json';        
           }
       }, 
       convert() {
        var self = this;
        self.result = '';    
        self.success = false;
           
        var engine = self.engine;
        if (engine === 'javascript')
        {
            self.parser.setAutoCasting(self.casting === 'on');    
            switch (self.joinChar)
            {
                case 'empty': self.parser.setMultilineJoinChar(''); break;
                case 'space': self.parser.setMultilineJoinChar(' '); break;
                case 'eol': self.parser.setMultilineJoinChar('\n'); break;    
            }
            let valuesTrue = ['true', 'yes', 'on', 't', 'y'];
            let valuesFalse = ['false', 'no', 'off', 'f', 'n'];
            if (self.val01asBool === 'on')
            {
                valuesTrue.push('1');
                valuesFalse.push('0');
            }
            self.parser.setBoolValues(valuesTrue, valuesFalse); 
                        
            setTimeout(function() {
                const start = performance.now();                        
                try 
                {
                    const result = self.parser.parse(self.tsinfo);                
                    self.result = self.rawdata === 'on' ? result.data : result;                  
                    self.success = true;
                    self.resultLng = self.format;
                }
                catch (error)
                {
                    self.success = false;
                    BootstrapToast.show({title: 'Błąd', message: 'Przepraszamy, ale wystąpił błąd parsowania składni: ' + error.message, when: 'teraz', type: 'text-bg-danger'});    
                }            
                const duration = performance.now() - start;
                self.time = duration.toFixed(2);
                self.percent = Math.min((duration / MAX_EXPECTED_TIME) * 100, 100).toFixed(0);
            
                if       (self.percent <= 25)                         self.bgColor = 'text-bg-success';
                else if ((self.percent > 25) && (self.percent <= 50)) self.bgColor = 'text-bg-info';
                else if ((self.percent > 50) && (self.percent <= 75)) self.bgColor = 'text-bg-warning';
                else                                                  self.bgColor = 'text-bg-danger';                
            }, 0);
                        
            return;
        }
           
        var data = {
            tsinfo: self.tsinfo,
            format: self.format,
            joinChar: self.joinChar,
            casting: self.casting,
            val01asBool: self.val01asBool,
            rawdata: self.rawdata,
            token: 'db40003a0be62f1a17782f05fb582d15245bbd58'
        };
                
        axios.post(urlSite('public/ajax/app/exec/28'), data, {headers: {'Content-Type': 'application/x-www-form-urlencoded'}})        
          .then(function (response) {              
              self.result = response.data.data;
              const duration = response.data.time;
              self.time = duration.toFixed(2);
              self.percent = Math.min((duration / MAX_EXPECTED_TIME) * 100, 100).toFixed(0);    

              if       (self.percent <= 25)                         self.bgColor = 'text-bg-success';
              else if ((self.percent > 25) && (self.percent <= 50)) self.bgColor = 'text-bg-info';
              else if ((self.percent > 50) && (self.percent <= 75)) self.bgColor = 'text-bg-warning';
              else                                                  self.bgColor = 'text-bg-danger';
            
              self.success = true;
              self.resultLng = self.format;
        }).catch(function(error) {
              self.success = false;
              BootstrapToast.show({title: 'Błąd', message: error.response.data.message, when: 'teraz', type: 'text-bg-danger'});
        });
      }
    }    
});

//CodeMirror for textarea
var myTextarea = document.getElementById('tsinfo');  
var editor = CodeMirror.fromTextArea(myTextarea, {
        lineNumbers: true,
        mode: "treestructinfo",
        indentUnit: 4,
        indentWithTabs: false,
        enterMode: "keep",
        tabMode: "shift",
        extraKeys: {Tab: false, "Shift-Tab": false}
  });
  editor.setSize(null, 500);  
  editor.getWrapperElement().style["font-size"] = "14px";
        
  editor.refresh();    
  editor.on('change', (args) => { vm.tsinfo = editor.getValue() } );  
    
  //WCAG missing form label fix  
  codemirrorTextareaIdFix("tsinfo");  
});    
</script>

Kod po stronie serwera

$tsinfo = Input::post('tsinfo', '');
$format = Input::post('format', 'php');
$join_char = Input::post('joinChar', 'empty');
$casting = Input::post('casting', 'off') === 'on';
$val01_as_bool = Input::post('val01asBool', 'off') === 'on';
$rawdata = Input::post('rawdata', 'off') === 'on';

$values_true = ['true', 'yes', 'on', 't', 'y'];
$values_false = ['false', 'no', 'off', 'f', 'n'];

switch ($join_char)
{
    case 'space': $join = ' '; break;    
    case 'eol': $join = PHP_EOL; break;
    default: $join = ''; break;    
}

if ($val01_as_bool)
{
    $values_true[] = '1';
    $values_false[] = '0';
}

$timediff = 0;
$start = microtime(true);

try
{    
    $array = Format::from_tsinfo($tsinfo, $casting, $join, [$values_true, $values_false]);        
    $result = $rawdata ? Arr::get($array, 'data', []) : $array;
}
catch (Exception $e)
{
    Kohana_Exception::log($e);
    throw HTTP_Exception::factory(500, json_encode(array('code' => 500, 'message' => 'Przepraszamy, ale wystąpił błąd parsowania składni: ' . $e->getMessage(), 'errors' => [])))
        ->as_json();    
}
    
switch ($format)
{
    case 'php': $dump = var_export($result, true); break;    
    case 'yaml': $dump = Format::to_yaml($result); break;
    case 'xml': 
        $xml = Format::to_xml($result); 
        //dump as pretty XML
        $dom = new DOMDocument('1.0');
        $dom->preserveWhiteSpace = true;
        $dom->formatOutput = true;
        $dom->loadXML($xml);
        $dump = $dom->saveXML();
        break;
    default: $dump = $result; break;        
}

$end = microtime(true);
$timediff = ($end - $start) * 1000;

$output = [
    'data' => $dump,
    'time' => $timediff,
];

$response = Response::factory()
    ->headers('Content-Type', 'application/json')
    ->status(200)
    ->body(json_encode($output));

echo $response->send_headers()->body();

Tagi

treestructinfo php javascript

Dziękujemy!
()

Informacja o cookies

Moja strona internetowa wykorzystuje wyłącznie niezbędne pliki cookies, które są wymagane do jej prawidłowego działania. Nie używam ciasteczek w celach marketingowych ani analitycznych. Korzystając z mojej strony, wyrażasz zgodę na stosowanie tych plików. Możesz dowiedzieć się więcej w mojej polityce prywatności.